Home | History | Annotate | Line # | Download | only in ftpd
ftpcmd.y revision 1.42
      1  1.42     lukem /*	$NetBSD: ftpcmd.y,v 1.42 1999/12/18 05:51:34 lukem Exp $	*/
      2  1.41     lukem 
      3  1.41     lukem /*-
      4  1.41     lukem  * Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
      5  1.41     lukem  * All rights reserved.
      6  1.41     lukem  *
      7  1.41     lukem  * This code is derived from software contributed to The NetBSD Foundation
      8  1.41     lukem  * by Luke Mewburn.
      9  1.41     lukem  *
     10  1.41     lukem  * Redistribution and use in source and binary forms, with or without
     11  1.41     lukem  * modification, are permitted provided that the following conditions
     12  1.41     lukem  * are met:
     13  1.41     lukem  * 1. Redistributions of source code must retain the above copyright
     14  1.41     lukem  *    notice, this list of conditions and the following disclaimer.
     15  1.41     lukem  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.41     lukem  *    notice, this list of conditions and the following disclaimer in the
     17  1.41     lukem  *    documentation and/or other materials provided with the distribution.
     18  1.41     lukem  * 3. All advertising materials mentioning features or use of this software
     19  1.41     lukem  *    must display the following acknowledgement:
     20  1.41     lukem  *        This product includes software developed by the NetBSD
     21  1.41     lukem  *        Foundation, Inc. and its contributors.
     22  1.41     lukem  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  1.41     lukem  *    contributors may be used to endorse or promote products derived
     24  1.41     lukem  *    from this software without specific prior written permission.
     25  1.41     lukem  *
     26  1.41     lukem  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  1.41     lukem  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  1.41     lukem  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  1.41     lukem  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  1.41     lukem  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  1.41     lukem  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  1.41     lukem  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  1.41     lukem  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  1.41     lukem  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  1.41     lukem  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  1.41     lukem  * POSSIBILITY OF SUCH DAMAGE.
     37  1.41     lukem  */
     38   1.5       cgd 
     39   1.1       cgd /*
     40   1.4   deraadt  * Copyright (c) 1985, 1988, 1993, 1994
     41   1.4   deraadt  *	The Regents of the University of California.  All rights reserved.
     42   1.1       cgd  *
     43   1.1       cgd  * Redistribution and use in source and binary forms, with or without
     44   1.1       cgd  * modification, are permitted provided that the following conditions
     45   1.1       cgd  * are met:
     46   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     47   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     48   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     49   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     50   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     51   1.1       cgd  * 3. All advertising materials mentioning features or use of this software
     52   1.1       cgd  *    must display the following acknowledgement:
     53   1.1       cgd  *	This product includes software developed by the University of
     54   1.1       cgd  *	California, Berkeley and its contributors.
     55   1.1       cgd  * 4. Neither the name of the University nor the names of its contributors
     56   1.1       cgd  *    may be used to endorse or promote products derived from this software
     57   1.1       cgd  *    without specific prior written permission.
     58   1.1       cgd  *
     59   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     60   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     61   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     62   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     63   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     64   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     65   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     66   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     67   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     68   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     69   1.1       cgd  * SUCH DAMAGE.
     70   1.1       cgd  *
     71   1.4   deraadt  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
     72   1.1       cgd  */
     73   1.1       cgd 
     74   1.1       cgd /*
     75   1.1       cgd  * Grammar for FTP commands.
     76   1.1       cgd  * See RFC 959.
     77   1.1       cgd  */
     78   1.1       cgd 
     79   1.1       cgd %{
     80  1.13  christos #include <sys/cdefs.h>
     81   1.1       cgd 
     82   1.1       cgd #ifndef lint
     83   1.5       cgd #if 0
     84   1.4   deraadt static char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
     85   1.5       cgd #else
     86  1.42     lukem __RCSID("$NetBSD: ftpcmd.y,v 1.42 1999/12/18 05:51:34 lukem Exp $");
     87   1.5       cgd #endif
     88   1.1       cgd #endif /* not lint */
     89   1.1       cgd 
     90   1.1       cgd #include <sys/param.h>
     91   1.1       cgd #include <sys/socket.h>
     92   1.1       cgd #include <sys/stat.h>
     93   1.4   deraadt 
     94   1.1       cgd #include <netinet/in.h>
     95   1.1       cgd #include <arpa/ftp.h>
     96  1.15       mrg #include <arpa/inet.h>
     97   1.4   deraadt 
     98   1.4   deraadt #include <ctype.h>
     99   1.4   deraadt #include <errno.h>
    100   1.4   deraadt #include <glob.h>
    101   1.4   deraadt #include <pwd.h>
    102   1.4   deraadt #include <setjmp.h>
    103   1.1       cgd #include <signal.h>
    104   1.4   deraadt #include <stdio.h>
    105   1.4   deraadt #include <stdlib.h>
    106   1.4   deraadt #include <string.h>
    107   1.1       cgd #include <syslog.h>
    108   1.1       cgd #include <time.h>
    109  1.18     lukem #include <tzfile.h>
    110   1.1       cgd #include <unistd.h>
    111  1.32    itojun #include <netdb.h>
    112  1.26  explorer 
    113  1.26  explorer #ifdef KERBEROS5
    114  1.36  christos #include <krb5/krb5.h>
    115  1.26  explorer #endif
    116   1.4   deraadt 
    117   1.4   deraadt #include "extern.h"
    118   1.1       cgd 
    119   1.1       cgd off_t	restart_point;
    120   1.1       cgd 
    121   1.1       cgd static	int cmd_type;
    122   1.1       cgd static	int cmd_form;
    123   1.1       cgd static	int cmd_bytesz;
    124   1.1       cgd char	cbuf[512];
    125   1.1       cgd char	*fromname;
    126  1.22     lukem int	hasyyerrored;
    127   1.1       cgd 
    128  1.24     lukem extern	jmp_buf		errcatch;
    129  1.24     lukem 
    130   1.1       cgd %}
    131   1.1       cgd 
    132   1.4   deraadt %union {
    133   1.4   deraadt 	int	i;
    134   1.4   deraadt 	char   *s;
    135   1.4   deraadt }
    136   1.4   deraadt 
    137   1.1       cgd %token
    138   1.1       cgd 	A	B	C	E	F	I
    139   1.1       cgd 	L	N	P	R	S	T
    140  1.32    itojun 	ALL
    141   1.1       cgd 
    142   1.4   deraadt 	SP	CRLF	COMMA
    143   1.1       cgd 
    144  1.23     lukem 	USER	PASS	ACCT	CWD	CDUP	SMNT
    145  1.23     lukem 	QUIT	REIN	PORT	PASV	TYPE	STRU
    146  1.23     lukem 	MODE	RETR	STOR	STOU	APPE	ALLO
    147  1.23     lukem 	REST	RNFR	RNTO	ABOR	DELE	RMD
    148  1.23     lukem 	MKD	PWD	LIST	NLST	SITE	SYST
    149  1.23     lukem 	STAT	HELP	NOOP
    150  1.23     lukem 
    151  1.25     lukem 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
    152  1.25     lukem 	CONF	ENC
    153  1.25     lukem 
    154  1.23     lukem 	FEAT	OPTS
    155  1.21     lukem 
    156  1.21     lukem 	SIZE	MDTM
    157   1.1       cgd 
    158  1.32    itojun 	LPRT	LPSV	EPRT	EPSV
    159  1.32    itojun 
    160  1.23     lukem 	MAIL	MLFL	MRCP	MRSQ	MSAM	MSND
    161  1.23     lukem 	MSOM
    162  1.23     lukem 
    163  1.41     lukem 	CHMOD	IDLE	RATEGET	RATEPUT	UMASK
    164   1.1       cgd 
    165   1.1       cgd 	LEXERR
    166   1.1       cgd 
    167   1.4   deraadt %token	<s> STRING
    168  1.32    itojun %token	<s> ALL
    169   1.4   deraadt %token	<i> NUMBER
    170   1.4   deraadt 
    171  1.41     lukem %type	<i> check_login check_modify check_upload octal_number byte_size
    172  1.25     lukem %type	<i> struct_code mode_code type_code form_code decimal_integer
    173   1.4   deraadt %type	<s> pathstring pathname password username
    174  1.25     lukem %type	<s> mechanism_name base64data prot_code
    175   1.4   deraadt 
    176   1.1       cgd %start	cmd_list
    177   1.1       cgd 
    178   1.1       cgd %%
    179   1.1       cgd 
    180   1.4   deraadt cmd_list
    181   1.4   deraadt 	: /* empty */
    182  1.23     lukem 
    183   1.4   deraadt 	| cmd_list cmd
    184   1.4   deraadt 		{
    185  1.22     lukem 			fromname = NULL;
    186   1.1       cgd 			restart_point = (off_t) 0;
    187   1.1       cgd 		}
    188  1.23     lukem 
    189   1.4   deraadt 	| cmd_list rcmd
    190  1.23     lukem 
    191   1.1       cgd 	;
    192   1.1       cgd 
    193   1.4   deraadt cmd
    194  1.23     lukem 						/* RFC 959 */
    195   1.4   deraadt 	: USER SP username CRLF
    196   1.4   deraadt 		{
    197   1.4   deraadt 			user($3);
    198   1.4   deraadt 			free($3);
    199   1.4   deraadt 		}
    200  1.23     lukem 
    201   1.4   deraadt 	| PASS SP password CRLF
    202   1.4   deraadt 		{
    203   1.4   deraadt 			pass($3);
    204  1.42     lukem 			memset($3, 0, strlen($3));
    205   1.4   deraadt 			free($3);
    206   1.1       cgd 		}
    207  1.23     lukem 
    208  1.23     lukem 	| CWD check_login CRLF
    209  1.23     lukem 		{
    210  1.23     lukem 			if ($2)
    211  1.23     lukem 				cwd(pw->pw_dir);
    212  1.23     lukem 		}
    213  1.23     lukem 
    214  1.23     lukem 	| CWD check_login SP pathname CRLF
    215  1.23     lukem 		{
    216  1.23     lukem 			if ($2 && $4 != NULL)
    217  1.23     lukem 				cwd($4);
    218  1.23     lukem 			if ($4 != NULL)
    219  1.23     lukem 				free($4);
    220  1.23     lukem 		}
    221  1.23     lukem 
    222  1.23     lukem 	| CDUP check_login CRLF
    223  1.23     lukem 		{
    224  1.23     lukem 			if ($2)
    225  1.23     lukem 				cwd("..");
    226  1.23     lukem 		}
    227  1.23     lukem 
    228  1.23     lukem 	| QUIT CRLF
    229  1.23     lukem 		{
    230  1.27     lukem 			if (logged_in) {
    231  1.28     lukem 				lreply(221, "");
    232  1.28     lukem 				lreply(0,
    233  1.27     lukem 	    "Data traffic for this session was %qd byte%s in %qd file%s.",
    234  1.30      ross 				    (qdfmt_t)total_data, PLURAL(total_data),
    235  1.30      ross 				    (qdfmt_t)total_files, PLURAL(total_files));
    236  1.28     lukem 				lreply(0,
    237  1.27     lukem 	    "Total traffic for this session was %qd byte%s in %qd transfer%s.",
    238  1.30      ross 				    (qdfmt_t)total_bytes, PLURAL(total_bytes),
    239  1.30      ross 				    (qdfmt_t)total_xfers, PLURAL(total_xfers));
    240  1.31     lukem 			}
    241  1.31     lukem 			reply(221,
    242  1.31     lukem 			    "Thank you for using the FTP service on %s.",
    243  1.31     lukem 			    hostname);
    244  1.31     lukem 			if (logged_in) {
    245  1.27     lukem 				syslog(LOG_INFO,
    246  1.27     lukem 				    "Data traffic: %qd byte%s in %qd file%s",
    247  1.30      ross 				    (qdfmt_t)total_data, PLURAL(total_data),
    248  1.30      ross 				    (qdfmt_t)total_files, PLURAL(total_files));
    249  1.27     lukem 				syslog(LOG_INFO,
    250  1.27     lukem 				  "Total traffic: %qd byte%s in %qd transfer%s",
    251  1.30      ross 				    (qdfmt_t)total_bytes, PLURAL(total_bytes),
    252  1.30      ross 				    (qdfmt_t)total_xfers, PLURAL(total_xfers));
    253  1.27     lukem 			}
    254  1.31     lukem 
    255  1.23     lukem 			dologout(0);
    256  1.23     lukem 		}
    257  1.23     lukem 
    258  1.15       mrg 	| PORT check_login SP host_port CRLF
    259   1.4   deraadt 		{
    260  1.22     lukem 			if ($2) {
    261  1.22     lukem 					/* be paranoid, if told so */
    262  1.16       mrg 			if (curclass.checkportcmd &&
    263  1.32    itojun 			    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
    264  1.32    itojun 			    memcmp(&data_dest.su_sin.sin_addr,
    265  1.32    itojun 			    &his_addr.su_sin.sin_addr,
    266  1.32    itojun 			    sizeof(data_dest.su_sin.sin_addr)) != 0)) {
    267  1.22     lukem 				reply(500,
    268  1.22     lukem 				    "Illegal PORT command rejected");
    269  1.32    itojun 			} else if (epsvall) {
    270  1.32    itojun 				reply(501, "PORT disallowed after EPSV ALL");
    271  1.22     lukem 			} else {
    272  1.22     lukem 				usedefault = 0;
    273  1.22     lukem 				if (pdata >= 0) {
    274  1.22     lukem 					(void) close(pdata);
    275  1.22     lukem 					pdata = -1;
    276  1.22     lukem 				}
    277  1.22     lukem 				reply(200, "PORT command successful.");
    278  1.15       mrg 			}
    279  1.32    itojun 
    280  1.32    itojun 			}
    281  1.32    itojun 		}
    282  1.32    itojun 
    283  1.34    itojun 	| LPRT check_login SP host_long_port4 CRLF
    284  1.32    itojun 		{
    285  1.42     lukem 			if ($2) {
    286  1.42     lukem 
    287  1.35    itojun 			/* reject invalid host_long_port4 */
    288  1.35    itojun 			if (data_dest.su_family != AF_INET) {
    289  1.35    itojun 				reply(500, "Illegal LPRT command rejected");
    290  1.35    itojun 				return (NULL);
    291  1.35    itojun 			}
    292  1.32    itojun 			/* be paranoid, if told so */
    293  1.32    itojun 			if (curclass.checkportcmd &&
    294  1.34    itojun 			    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
    295  1.34    itojun 			     memcmp(&data_dest.su_sin.sin_addr,
    296  1.34    itojun 				    &his_addr.su_sin.sin_addr,
    297  1.34    itojun 			     sizeof(data_dest.su_sin.sin_addr)) != 0)) {
    298  1.34    itojun 				reply(500, "Illegal LPRT command rejected");
    299  1.34    itojun 				return (NULL);
    300  1.34    itojun 			}
    301  1.34    itojun 			if (epsvall)
    302  1.34    itojun 				reply(501, "LPRT disallowed after EPSV ALL");
    303  1.34    itojun 			else {
    304  1.34    itojun 				usedefault = 0;
    305  1.34    itojun 				if (pdata >= 0) {
    306  1.34    itojun 					(void) close(pdata);
    307  1.34    itojun 					pdata = -1;
    308  1.34    itojun 				}
    309  1.34    itojun 				reply(200, "LPRT command successful.");
    310  1.34    itojun 			}
    311  1.42     lukem 
    312  1.42     lukem 			}
    313  1.34    itojun 		}
    314  1.34    itojun 
    315  1.34    itojun 	| LPRT check_login SP host_long_port6 CRLF
    316  1.34    itojun 		{
    317  1.42     lukem 			if ($2) {
    318  1.42     lukem 
    319  1.35    itojun 			/* reject invalid host_long_port6 */
    320  1.35    itojun 			if (data_dest.su_family != AF_INET6) {
    321  1.35    itojun 				reply(500, "Illegal LPRT command rejected");
    322  1.35    itojun 				return (NULL);
    323  1.35    itojun 			}
    324  1.34    itojun 			/* be paranoid, if told so */
    325  1.34    itojun 			if (curclass.checkportcmd &&
    326  1.34    itojun 			    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
    327  1.34    itojun 			     memcmp(&data_dest.su_sin6.sin6_addr,
    328  1.34    itojun 				    &his_addr.su_sin6.sin6_addr,
    329  1.34    itojun 			     sizeof(data_dest.su_sin6.sin6_addr)) != 0)) {
    330  1.32    itojun 				reply(500, "Illegal LPRT command rejected");
    331  1.32    itojun 				return (NULL);
    332  1.32    itojun 			}
    333  1.32    itojun 			if (epsvall)
    334  1.32    itojun 				reply(501, "LPRT disallowed after EPSV ALL");
    335  1.32    itojun 			else {
    336  1.32    itojun 				usedefault = 0;
    337  1.32    itojun 				if (pdata >= 0) {
    338  1.32    itojun 					(void) close(pdata);
    339  1.32    itojun 					pdata = -1;
    340  1.32    itojun 				}
    341  1.32    itojun 				reply(200, "LPRT command successful.");
    342   1.1       cgd 			}
    343  1.42     lukem 
    344  1.42     lukem 			}
    345   1.1       cgd 		}
    346  1.23     lukem 
    347  1.32    itojun 	| EPRT check_login SP STRING CRLF
    348  1.32    itojun 		{
    349  1.32    itojun 			char *tmp = NULL;
    350  1.32    itojun 			char *result[3];
    351  1.32    itojun 			char *p, *q;
    352  1.32    itojun 			char delim;
    353  1.32    itojun 			struct addrinfo hints;
    354  1.32    itojun 			struct addrinfo *res;
    355  1.32    itojun 			int i;
    356  1.32    itojun 
    357  1.42     lukem 			if ($2) {
    358  1.42     lukem 
    359  1.32    itojun 			if (epsvall) {
    360  1.32    itojun 				reply(501, "EPRT disallowed after EPSV ALL");
    361  1.32    itojun 				goto eprt_done;
    362  1.32    itojun 			}
    363  1.32    itojun 			usedefault = 0;
    364  1.32    itojun 			if (pdata >= 0) {
    365  1.32    itojun 				(void) close(pdata);
    366  1.32    itojun 				pdata = -1;
    367  1.32    itojun 			}
    368  1.32    itojun 
    369  1.40     lukem 			tmp = xstrdup($4);
    370  1.32    itojun 			p = tmp;
    371  1.32    itojun 			delim = p[0];
    372  1.32    itojun 			p++;
    373  1.32    itojun 			memset(result, 0, sizeof(result));
    374  1.32    itojun 			for (i = 0; i < 3; i++) {
    375  1.32    itojun 				q = strchr(p, delim);
    376  1.32    itojun 				if (!q || *q != delim) {
    377  1.32    itojun 		parsefail:
    378  1.42     lukem 					reply(500,
    379  1.42     lukem 					    "Invalid argument, rejected.");
    380  1.32    itojun 					usedefault = 1;
    381  1.32    itojun 					goto eprt_done;
    382  1.32    itojun 				}
    383  1.32    itojun 				*q++ = '\0';
    384  1.32    itojun 				result[i] = p;
    385  1.32    itojun 				p = q;
    386  1.32    itojun 			}
    387  1.32    itojun 
    388  1.32    itojun 			/* some more sanity check */
    389  1.32    itojun 			p = result[0];
    390  1.32    itojun 			while (*p) {
    391  1.32    itojun 				if (!isdigit(*p))
    392  1.32    itojun 					goto parsefail;
    393  1.32    itojun 				p++;
    394  1.32    itojun 			}
    395  1.32    itojun 			p = result[2];
    396  1.32    itojun 			while (*p) {
    397  1.32    itojun 				if (!isdigit(*p))
    398  1.32    itojun 					goto parsefail;
    399  1.32    itojun 				p++;
    400  1.32    itojun 			}
    401  1.32    itojun 
    402  1.32    itojun 			memset(&hints, 0, sizeof(hints));
    403  1.32    itojun 			if (atoi(result[0]) == 1)
    404  1.32    itojun 				hints.ai_family = PF_INET;
    405  1.32    itojun 			if (atoi(result[0]) == 2)
    406  1.32    itojun 				hints.ai_family = PF_INET6;
    407  1.32    itojun 			else
    408  1.32    itojun 				hints.ai_family = PF_UNSPEC;	/*XXX*/
    409  1.32    itojun 			hints.ai_socktype = SOCK_STREAM;
    410  1.32    itojun 			if (getaddrinfo(result[1], result[2], &hints, &res))
    411  1.32    itojun 				goto parsefail;
    412  1.32    itojun 			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
    413  1.37    itojun 			if (his_addr.su_family == AF_INET6
    414  1.37    itojun 			 && data_dest.su_family == AF_INET6) {
    415  1.37    itojun 				/* XXX more sanity checks! */
    416  1.37    itojun 				data_dest.su_sin6.sin6_scope_id =
    417  1.37    itojun 					his_addr.su_sin6.sin6_scope_id;
    418  1.37    itojun 			}
    419  1.32    itojun 			/* be paranoid, if told so */
    420  1.32    itojun 			if (curclass.checkportcmd) {
    421  1.32    itojun 				int fail;
    422  1.32    itojun 				fail = 0;
    423  1.32    itojun 				if (ntohs(data_dest.su_port) < IPPORT_RESERVED)
    424  1.32    itojun 					fail++;
    425  1.32    itojun 				if (data_dest.su_family != his_addr.su_family)
    426  1.32    itojun 					fail++;
    427  1.32    itojun 				if (data_dest.su_len != his_addr.su_len)
    428  1.32    itojun 					fail++;
    429  1.32    itojun 				switch (data_dest.su_family) {
    430  1.32    itojun 				case AF_INET:
    431  1.42     lukem 					fail += memcmp(
    432  1.42     lukem 					    &data_dest.su_sin.sin_addr,
    433  1.32    itojun 					    &his_addr.su_sin.sin_addr,
    434  1.32    itojun 					    sizeof(data_dest.su_sin.sin_addr));
    435  1.32    itojun 					break;
    436  1.32    itojun 				case AF_INET6:
    437  1.42     lukem 					fail += memcmp(
    438  1.42     lukem 					    &data_dest.su_sin6.sin6_addr,
    439  1.32    itojun 					    &his_addr.su_sin6.sin6_addr,
    440  1.32    itojun 					    sizeof(data_dest.su_sin6.sin6_addr));
    441  1.32    itojun 					break;
    442  1.32    itojun 				default:
    443  1.32    itojun 					fail++;
    444  1.32    itojun 				}
    445  1.32    itojun 				if (fail) {
    446  1.32    itojun 					reply(500,
    447  1.32    itojun 					    "Illegal EPRT command rejected");
    448  1.32    itojun 					return (NULL);
    449  1.32    itojun 				}
    450  1.32    itojun 			}
    451  1.33    itojun 			if (pdata >= 0) {
    452  1.33    itojun 				(void) close(pdata);
    453  1.33    itojun 				pdata = -1;
    454  1.33    itojun 			}
    455  1.32    itojun 			reply(200, "EPRT command successful.");
    456  1.32    itojun 		eprt_done:;
    457  1.42     lukem 			if (tmp != NULL)
    458  1.42     lukem 				free(tmp);
    459  1.42     lukem 
    460  1.42     lukem 			}
    461  1.42     lukem 			free($4);
    462  1.32    itojun 		}
    463  1.32    itojun 
    464  1.20        tv 	| PASV check_login CRLF
    465   1.4   deraadt 		{
    466  1.42     lukem 			if ($2) {
    467  1.42     lukem 				if (curclass.passive)
    468  1.42     lukem 					passive();
    469  1.42     lukem 				else
    470  1.42     lukem 					reply(500, "PASV mode not available.");
    471  1.20        tv 			}
    472   1.1       cgd 		}
    473  1.23     lukem 
    474  1.42     lukem 	| LPSV check_login CRLF
    475  1.32    itojun 		{
    476  1.42     lukem 			if ($2) {
    477  1.42     lukem 				if (epsvall)
    478  1.42     lukem 					reply(501,
    479  1.42     lukem 					    "LPSV disallowed after EPSV ALL");
    480  1.42     lukem 				else
    481  1.42     lukem 					long_passive("LPSV", PF_UNSPEC);
    482  1.42     lukem 			}
    483  1.32    itojun 		}
    484  1.32    itojun 
    485  1.42     lukem 	| EPSV check_login SP NUMBER CRLF
    486  1.32    itojun 		{
    487  1.42     lukem 			if ($2) {
    488  1.42     lukem 				int pf;
    489  1.42     lukem 
    490  1.42     lukem 				switch ($4) {
    491  1.42     lukem 				case 1:
    492  1.42     lukem 					pf = PF_INET;
    493  1.42     lukem 					break;
    494  1.42     lukem 				case 2:
    495  1.42     lukem 					pf = PF_INET6;
    496  1.42     lukem 					break;
    497  1.42     lukem 				default:
    498  1.42     lukem 					pf = -1;	/*junk*/
    499  1.42     lukem 					break;
    500  1.42     lukem 				}
    501  1.42     lukem 				long_passive("EPSV", pf);
    502  1.32    itojun 			}
    503  1.32    itojun 		}
    504  1.32    itojun 
    505  1.42     lukem 	| EPSV check_login SP ALL CRLF
    506  1.32    itojun 		{
    507  1.42     lukem 			if ($2) {
    508  1.32    itojun 				reply(200, "EPSV ALL command successful.");
    509  1.32    itojun 				epsvall++;
    510  1.32    itojun 			}
    511  1.32    itojun 		}
    512  1.32    itojun 
    513  1.42     lukem 	| EPSV check_login CRLF
    514  1.32    itojun 		{
    515  1.42     lukem 			if ($2)
    516  1.42     lukem 				long_passive("EPSV", PF_UNSPEC);
    517  1.32    itojun 		}
    518  1.32    itojun 
    519  1.42     lukem 	| TYPE check_login SP type_code CRLF
    520   1.4   deraadt 		{
    521  1.42     lukem 			if ($2) {
    522  1.42     lukem 
    523   1.1       cgd 			switch (cmd_type) {
    524   1.1       cgd 
    525   1.1       cgd 			case TYPE_A:
    526   1.1       cgd 				if (cmd_form == FORM_N) {
    527   1.1       cgd 					reply(200, "Type set to A.");
    528   1.1       cgd 					type = cmd_type;
    529   1.1       cgd 					form = cmd_form;
    530   1.1       cgd 				} else
    531   1.1       cgd 					reply(504, "Form must be N.");
    532   1.1       cgd 				break;
    533   1.1       cgd 
    534   1.1       cgd 			case TYPE_E:
    535   1.1       cgd 				reply(504, "Type E not implemented.");
    536   1.1       cgd 				break;
    537   1.1       cgd 
    538   1.1       cgd 			case TYPE_I:
    539   1.1       cgd 				reply(200, "Type set to I.");
    540   1.1       cgd 				type = cmd_type;
    541   1.1       cgd 				break;
    542   1.1       cgd 
    543   1.1       cgd 			case TYPE_L:
    544   1.1       cgd #if NBBY == 8
    545   1.1       cgd 				if (cmd_bytesz == 8) {
    546   1.1       cgd 					reply(200,
    547   1.1       cgd 					    "Type set to L (byte size 8).");
    548   1.1       cgd 					type = cmd_type;
    549   1.1       cgd 				} else
    550   1.1       cgd 					reply(504, "Byte size must be 8.");
    551   1.1       cgd #else /* NBBY == 8 */
    552   1.1       cgd 				UNIMPLEMENTED for NBBY != 8
    553   1.1       cgd #endif /* NBBY == 8 */
    554   1.1       cgd 			}
    555  1.42     lukem 
    556  1.42     lukem 			}
    557   1.1       cgd 		}
    558  1.23     lukem 
    559  1.42     lukem 	| STRU check_login SP struct_code CRLF
    560   1.4   deraadt 		{
    561  1.42     lukem 			if ($2) {
    562  1.42     lukem 				switch ($4) {
    563   1.1       cgd 
    564  1.42     lukem 				case STRU_F:
    565  1.42     lukem 					reply(200, "STRU F ok.");
    566  1.42     lukem 					break;
    567   1.1       cgd 
    568  1.42     lukem 				default:
    569  1.42     lukem 					reply(504, "Unimplemented STRU type.");
    570  1.42     lukem 				}
    571   1.1       cgd 			}
    572   1.1       cgd 		}
    573  1.23     lukem 
    574  1.42     lukem 	| MODE check_login SP mode_code CRLF
    575   1.4   deraadt 		{
    576  1.42     lukem 			if ($2) {
    577  1.42     lukem 				switch ($4) {
    578   1.1       cgd 
    579  1.42     lukem 				case MODE_S:
    580  1.42     lukem 					reply(200, "MODE S ok.");
    581  1.42     lukem 					break;
    582   1.1       cgd 
    583  1.42     lukem 				default:
    584  1.42     lukem 					reply(502, "Unimplemented MODE type.");
    585  1.42     lukem 				}
    586   1.1       cgd 			}
    587   1.1       cgd 		}
    588  1.23     lukem 
    589   1.4   deraadt 	| RETR check_login SP pathname CRLF
    590   1.4   deraadt 		{
    591   1.1       cgd 			if ($2 && $4 != NULL)
    592  1.22     lukem 				retrieve(NULL, $4);
    593   1.1       cgd 			if ($4 != NULL)
    594   1.4   deraadt 				free($4);
    595   1.1       cgd 		}
    596  1.23     lukem 
    597  1.41     lukem 	| STOR check_upload SP pathname CRLF
    598   1.4   deraadt 		{
    599   1.1       cgd 			if ($2 && $4 != NULL)
    600   1.4   deraadt 				store($4, "w", 0);
    601   1.1       cgd 			if ($4 != NULL)
    602   1.4   deraadt 				free($4);
    603   1.1       cgd 		}
    604  1.23     lukem 
    605  1.41     lukem 	| STOU check_upload SP pathname CRLF
    606   1.4   deraadt 		{
    607   1.1       cgd 			if ($2 && $4 != NULL)
    608  1.23     lukem 				store($4, "w", 1);
    609   1.1       cgd 			if ($4 != NULL)
    610   1.4   deraadt 				free($4);
    611   1.1       cgd 		}
    612  1.23     lukem 
    613  1.41     lukem 	| APPE check_upload SP pathname CRLF
    614   1.4   deraadt 		{
    615   1.4   deraadt 			if ($2 && $4 != NULL)
    616  1.23     lukem 				store($4, "a", 0);
    617   1.1       cgd 			if ($4 != NULL)
    618   1.4   deraadt 				free($4);
    619   1.1       cgd 		}
    620  1.23     lukem 
    621  1.42     lukem 	| ALLO check_login SP NUMBER CRLF
    622   1.4   deraadt 		{
    623  1.42     lukem 			if ($2)
    624  1.42     lukem 				reply(202, "ALLO command ignored.");
    625   1.1       cgd 		}
    626  1.23     lukem 
    627  1.42     lukem 	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
    628   1.4   deraadt 		{
    629  1.42     lukem 			if ($2)
    630  1.42     lukem 				reply(202, "ALLO command ignored.");
    631   1.1       cgd 		}
    632  1.23     lukem 
    633  1.42     lukem 	| RNTO check_login SP pathname CRLF
    634   1.4   deraadt 		{
    635  1.42     lukem 			if ($2) {
    636  1.42     lukem 				if (fromname) {
    637  1.42     lukem 					renamecmd(fromname, $4);
    638  1.42     lukem 					free(fromname);
    639  1.42     lukem 					fromname = NULL;
    640  1.42     lukem 				} else {
    641  1.42     lukem 					reply(503, "Bad sequence of commands.");
    642  1.42     lukem 				}
    643   1.1       cgd 			}
    644  1.42     lukem 			free($4);
    645   1.1       cgd 		}
    646  1.23     lukem 
    647  1.42     lukem 	| ABOR check_login CRLF
    648   1.4   deraadt 		{
    649  1.42     lukem 			if ($2)
    650  1.42     lukem 				reply(225, "ABOR command successful.");
    651   1.1       cgd 		}
    652  1.23     lukem 
    653  1.23     lukem 	| DELE check_modify SP pathname CRLF
    654   1.4   deraadt 		{
    655  1.23     lukem 			if ($2 && $4 != NULL)
    656  1.23     lukem 				delete($4);
    657  1.23     lukem 			if ($4 != NULL)
    658  1.23     lukem 				free($4);
    659   1.1       cgd 		}
    660  1.23     lukem 
    661  1.23     lukem 	| RMD check_modify SP pathname CRLF
    662   1.4   deraadt 		{
    663   1.1       cgd 			if ($2 && $4 != NULL)
    664  1.23     lukem 				removedir($4);
    665   1.1       cgd 			if ($4 != NULL)
    666   1.4   deraadt 				free($4);
    667   1.1       cgd 		}
    668   1.1       cgd 
    669  1.12     lukem 	| MKD check_modify SP pathname CRLF
    670   1.4   deraadt 		{
    671   1.1       cgd 			if ($2 && $4 != NULL)
    672   1.4   deraadt 				makedir($4);
    673   1.1       cgd 			if ($4 != NULL)
    674   1.4   deraadt 				free($4);
    675   1.1       cgd 		}
    676  1.23     lukem 
    677  1.23     lukem 	| PWD check_login CRLF
    678  1.23     lukem 		{
    679  1.23     lukem 			if ($2)
    680  1.23     lukem 				pwd();
    681  1.23     lukem 		}
    682  1.23     lukem 
    683  1.23     lukem 	| LIST check_login CRLF
    684  1.23     lukem 		{
    685  1.40     lukem 			char *argv[] = { INTERNAL_LS, "-lgA", NULL };
    686  1.40     lukem 
    687  1.23     lukem 			if ($2)
    688  1.40     lukem 				retrieve(argv, "");
    689  1.23     lukem 		}
    690  1.23     lukem 
    691  1.23     lukem 	| LIST check_login SP pathname CRLF
    692   1.4   deraadt 		{
    693  1.40     lukem 			char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
    694  1.40     lukem 
    695  1.40     lukem 			if ($2 && $4 != NULL) {
    696  1.40     lukem 				argv[2] = $4;
    697  1.40     lukem 				retrieve(argv, $4);
    698  1.40     lukem 			}
    699   1.1       cgd 			if ($4 != NULL)
    700   1.4   deraadt 				free($4);
    701   1.1       cgd 		}
    702  1.23     lukem 
    703  1.23     lukem 	| NLST check_login CRLF
    704   1.4   deraadt 		{
    705   1.1       cgd 			if ($2)
    706  1.23     lukem 				send_file_list(".");
    707   1.1       cgd 		}
    708  1.23     lukem 
    709  1.23     lukem 	| NLST check_login SP STRING CRLF
    710   1.4   deraadt 		{
    711  1.42     lukem 			if ($2)
    712  1.23     lukem 				send_file_list($4);
    713  1.42     lukem 			free($4);
    714   1.1       cgd 		}
    715  1.23     lukem 
    716   1.4   deraadt 	| SITE SP HELP CRLF
    717   1.4   deraadt 		{
    718  1.22     lukem 			help(sitetab, NULL);
    719   1.1       cgd 		}
    720  1.23     lukem 
    721  1.12     lukem 	| SITE SP CHMOD check_modify SP octal_number SP pathname CRLF
    722   1.4   deraadt 		{
    723   1.1       cgd 			if ($4 && ($8 != NULL)) {
    724   1.1       cgd 				if ($6 > 0777)
    725   1.1       cgd 					reply(501,
    726   1.1       cgd 				"CHMOD: Mode value must be between 0 and 0777");
    727   1.4   deraadt 				else if (chmod($8, $6) < 0)
    728   1.4   deraadt 					perror_reply(550, $8);
    729   1.1       cgd 				else
    730   1.1       cgd 					reply(200, "CHMOD command successful.");
    731   1.1       cgd 			}
    732   1.1       cgd 			if ($8 != NULL)
    733   1.4   deraadt 				free($8);
    734   1.1       cgd 		}
    735  1.23     lukem 
    736  1.41     lukem 	| SITE SP HELP SP STRING CRLF
    737  1.41     lukem 		{
    738  1.41     lukem 			help(sitetab, $5);
    739  1.42     lukem 			free($5);
    740  1.41     lukem 		}
    741  1.41     lukem 
    742  1.42     lukem 	| SITE SP IDLE check_login CRLF
    743   1.4   deraadt 		{
    744  1.42     lukem 			if ($4) {
    745  1.42     lukem 				reply(200,
    746   1.1       cgd 			    "Current IDLE time limit is %d seconds; max %d",
    747  1.42     lukem 				    curclass.timeout, curclass.maxtimeout);
    748  1.42     lukem 			}
    749   1.1       cgd 		}
    750  1.23     lukem 
    751  1.42     lukem 	| SITE SP IDLE check_login SP NUMBER CRLF
    752   1.4   deraadt 		{
    753  1.42     lukem 			if ($4) {
    754  1.42     lukem 				if ($6 < 30 || $6 > curclass.maxtimeout) {
    755  1.42     lukem 					reply(501,
    756  1.42     lukem 			    "IDLE time limit must be between 30 and %d seconds",
    757  1.42     lukem 					    curclass.maxtimeout);
    758  1.42     lukem 				} else {
    759  1.42     lukem 					curclass.timeout = $6;
    760  1.42     lukem 					(void) alarm(curclass.timeout);
    761  1.42     lukem 					reply(200,
    762  1.42     lukem 					    "IDLE time limit set to %d seconds",
    763  1.42     lukem 					    curclass.timeout);
    764  1.42     lukem 				}
    765   1.1       cgd 			}
    766   1.1       cgd 		}
    767  1.23     lukem 
    768  1.42     lukem 	| SITE SP RATEGET check_login CRLF
    769  1.41     lukem 		{
    770  1.42     lukem 			if ($4) {
    771  1.42     lukem 				reply(200, "Current RATEGET is %d bytes/sec",
    772  1.42     lukem 				    curclass.rateget);
    773  1.42     lukem 			}
    774  1.41     lukem 		}
    775  1.41     lukem 
    776  1.42     lukem 	| SITE SP RATEGET check_login SP STRING CRLF
    777  1.41     lukem 		{
    778  1.42     lukem 			char *p = $6;
    779  1.41     lukem 			int rate;
    780  1.41     lukem 
    781  1.42     lukem 			if ($4) {
    782  1.42     lukem 				rate = strsuftoi(p);
    783  1.42     lukem 				if (rate == -1)
    784  1.42     lukem 					reply(501, "Invalid RATEGET %s", p);
    785  1.42     lukem 				else if (curclass.maxrateget &&
    786  1.42     lukem 				    rate > curclass.maxrateget)
    787  1.42     lukem 					reply(501,
    788  1.42     lukem 				"RATEGET %d is larger than maximum RATEGET %d",
    789  1.42     lukem 					    rate, curclass.maxrateget);
    790  1.42     lukem 				else {
    791  1.42     lukem 					curclass.rateget = rate;
    792  1.42     lukem 					reply(200,
    793  1.42     lukem 					    "RATEGET set to %d bytes/sec",
    794  1.42     lukem 					    curclass.rateget);
    795  1.42     lukem 				}
    796  1.41     lukem 			}
    797  1.42     lukem 			free($6);
    798  1.41     lukem 		}
    799  1.41     lukem 
    800  1.42     lukem 	| SITE SP RATEPUT check_login CRLF
    801  1.41     lukem 		{
    802  1.42     lukem 			if ($4) {
    803  1.42     lukem 				reply(200, "Current RATEPUT is %d bytes/sec",
    804  1.42     lukem 				    curclass.rateput);
    805  1.42     lukem 			}
    806  1.41     lukem 		}
    807  1.41     lukem 
    808  1.42     lukem 	| SITE SP RATEPUT check_login SP STRING CRLF
    809  1.41     lukem 		{
    810  1.42     lukem 			char *p = $6;
    811  1.41     lukem 			int rate;
    812  1.41     lukem 
    813  1.42     lukem 			if ($4) {
    814  1.42     lukem 				rate = strsuftoi(p);
    815  1.42     lukem 				if (rate == -1)
    816  1.42     lukem 					reply(501, "Invalid RATEPUT %s", p);
    817  1.42     lukem 				else if (curclass.maxrateput &&
    818  1.42     lukem 				    rate > curclass.maxrateput)
    819  1.42     lukem 					reply(501,
    820  1.42     lukem 				"RATEPUT %d is larger than maximum RATEPUT %d",
    821  1.42     lukem 					    rate, curclass.maxrateput);
    822  1.42     lukem 				else {
    823  1.42     lukem 					curclass.rateput = rate;
    824  1.42     lukem 					reply(200,
    825  1.42     lukem 					    "RATEPUT set to %d bytes/sec",
    826  1.42     lukem 					    curclass.rateput);
    827  1.42     lukem 				}
    828  1.41     lukem 			}
    829  1.42     lukem 			free($6);
    830  1.41     lukem 		}
    831  1.41     lukem 
    832  1.41     lukem 	| SITE SP UMASK check_login CRLF
    833  1.41     lukem 		{
    834  1.41     lukem 			int oldmask;
    835  1.41     lukem 
    836  1.41     lukem 			if ($4) {
    837  1.41     lukem 				oldmask = umask(0);
    838  1.41     lukem 				(void) umask(oldmask);
    839  1.41     lukem 				reply(200, "Current UMASK is %03o", oldmask);
    840  1.41     lukem 			}
    841  1.41     lukem 		}
    842  1.41     lukem 
    843  1.41     lukem 	| SITE SP UMASK check_modify SP octal_number CRLF
    844  1.41     lukem 		{
    845  1.41     lukem 			int oldmask;
    846  1.41     lukem 
    847  1.41     lukem 			if ($4) {
    848  1.41     lukem 				if (($6 == -1) || ($6 > 0777)) {
    849  1.41     lukem 					reply(501, "Bad UMASK value");
    850  1.41     lukem 				} else {
    851  1.41     lukem 					oldmask = umask($6);
    852  1.41     lukem 					reply(200,
    853  1.41     lukem 					    "UMASK set to %03o (was %03o)",
    854  1.41     lukem 					    $6, oldmask);
    855  1.41     lukem 				}
    856  1.41     lukem 			}
    857  1.41     lukem 		}
    858  1.41     lukem 
    859  1.23     lukem 	| SYST CRLF
    860  1.23     lukem 		{
    861  1.23     lukem 			reply(215, "UNIX Type: L%d %s", NBBY, version);
    862  1.23     lukem 		}
    863  1.23     lukem 
    864  1.23     lukem 	| STAT check_login SP pathname CRLF
    865   1.4   deraadt 		{
    866   1.1       cgd 			if ($2 && $4 != NULL)
    867  1.23     lukem 				statfilecmd($4);
    868   1.1       cgd 			if ($4 != NULL)
    869   1.4   deraadt 				free($4);
    870   1.1       cgd 		}
    871  1.23     lukem 
    872  1.23     lukem 	| STAT CRLF
    873  1.23     lukem 		{
    874  1.23     lukem 			statcmd();
    875  1.23     lukem 		}
    876  1.23     lukem 
    877  1.23     lukem 	| HELP CRLF
    878  1.23     lukem 		{
    879  1.23     lukem 			help(cmdtab, NULL);
    880  1.23     lukem 		}
    881  1.23     lukem 
    882  1.23     lukem 	| HELP SP STRING CRLF
    883  1.23     lukem 		{
    884  1.23     lukem 			char *cp = $3;
    885  1.23     lukem 
    886  1.23     lukem 			if (strncasecmp(cp, "SITE", 4) == 0) {
    887  1.23     lukem 				cp = $3 + 4;
    888  1.23     lukem 				if (*cp == ' ')
    889  1.23     lukem 					cp++;
    890  1.23     lukem 				if (*cp)
    891  1.23     lukem 					help(sitetab, cp);
    892  1.23     lukem 				else
    893  1.23     lukem 					help(sitetab, NULL);
    894  1.23     lukem 			} else
    895  1.23     lukem 				help(cmdtab, $3);
    896  1.42     lukem 			free($3);
    897  1.23     lukem 		}
    898  1.23     lukem 
    899  1.23     lukem 	| NOOP CRLF
    900  1.23     lukem 		{
    901  1.23     lukem 			reply(200, "NOOP command successful.");
    902  1.23     lukem 		}
    903  1.23     lukem 
    904  1.25     lukem 						/* RFC 2228 */
    905  1.25     lukem 	| AUTH SP mechanism_name CRLF
    906  1.25     lukem 		{
    907  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    908  1.42     lukem 			free($3);
    909  1.25     lukem 		}
    910  1.25     lukem 
    911  1.25     lukem 	| ADAT SP base64data CRLF
    912  1.25     lukem 		{
    913  1.25     lukem 			reply(503,
    914  1.25     lukem 			    "Please set authentication state with AUTH.");
    915  1.42     lukem 			free($3);
    916  1.25     lukem 		}
    917  1.25     lukem 
    918  1.25     lukem 	| PROT SP prot_code CRLF
    919  1.25     lukem 		{
    920  1.25     lukem 			reply(503,
    921  1.25     lukem 			    "Please set protection buffer size with PBSZ.");
    922  1.42     lukem 			free($3);
    923  1.25     lukem 		}
    924  1.25     lukem 
    925  1.25     lukem 	| PBSZ SP decimal_integer CRLF
    926  1.25     lukem 		{
    927  1.25     lukem 			reply(503,
    928  1.25     lukem 			    "Please set authentication state with AUTH.");
    929  1.25     lukem 		}
    930  1.25     lukem 
    931  1.25     lukem 	| CCC CRLF
    932  1.25     lukem 		{
    933  1.25     lukem 			reply(533, "No protection enabled.");
    934  1.25     lukem 		}
    935  1.25     lukem 
    936  1.25     lukem 	| MIC SP base64data CRLF
    937  1.25     lukem 		{
    938  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    939  1.42     lukem 			free($3);
    940  1.25     lukem 		}
    941  1.25     lukem 
    942  1.25     lukem 	| CONF SP base64data CRLF
    943  1.25     lukem 		{
    944  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    945  1.42     lukem 			free($3);
    946  1.25     lukem 		}
    947  1.25     lukem 
    948  1.25     lukem 	| ENC SP base64data CRLF
    949  1.25     lukem 		{
    950  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    951  1.42     lukem 			free($3);
    952  1.25     lukem 		}
    953  1.25     lukem 
    954  1.23     lukem 						/* RFC 2389 */
    955  1.23     lukem 	| FEAT CRLF
    956  1.23     lukem 		{
    957  1.23     lukem 			lreply(211, "Features supported");
    958  1.27     lukem 			lreply(-1,  " MDTM");
    959  1.27     lukem 			lreply(-1,  " REST STREAM");
    960  1.27     lukem 			lreply(-1,  " SIZE");
    961  1.27     lukem 			reply(211,  "End");
    962  1.23     lukem 		}
    963  1.23     lukem 
    964  1.23     lukem 	| OPTS SP STRING CRLF
    965   1.4   deraadt 		{
    966  1.23     lukem 
    967  1.23     lukem 			opts($3);
    968  1.42     lukem 			free($3);
    969   1.1       cgd 		}
    970   1.1       cgd 
    971  1.23     lukem 
    972  1.23     lukem 						/* BSD extensions */
    973  1.23     lukem 
    974   1.1       cgd 		/*
    975  1.21     lukem 		 * SIZE is not in RFC 959, but Postel has blessed it and
    976   1.1       cgd 		 * it will be in the updated RFC.
    977   1.1       cgd 		 *
    978   1.1       cgd 		 * Return size of file in a format suitable for
    979   1.1       cgd 		 * using with RESTART (we just count bytes).
    980   1.1       cgd 		 */
    981   1.4   deraadt 	| SIZE check_login SP pathname CRLF
    982   1.4   deraadt 		{
    983   1.1       cgd 			if ($2 && $4 != NULL)
    984   1.4   deraadt 				sizecmd($4);
    985   1.1       cgd 			if ($4 != NULL)
    986   1.4   deraadt 				free($4);
    987   1.1       cgd 		}
    988   1.1       cgd 
    989   1.1       cgd 		/*
    990  1.21     lukem 		 * MDTM is not in RFC 959, but Postel has blessed it and
    991   1.1       cgd 		 * it will be in the updated RFC.
    992   1.1       cgd 		 *
    993   1.1       cgd 		 * Return modification time of file as an ISO 3307
    994   1.1       cgd 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
    995   1.1       cgd 		 * where xxx is the fractional second (of any precision,
    996   1.1       cgd 		 * not necessarily 3 digits)
    997   1.1       cgd 		 */
    998   1.4   deraadt 	| MDTM check_login SP pathname CRLF
    999   1.4   deraadt 		{
   1000   1.1       cgd 			if ($2 && $4 != NULL) {
   1001   1.1       cgd 				struct stat stbuf;
   1002   1.4   deraadt 				if (stat($4, &stbuf) < 0)
   1003  1.22     lukem 					perror_reply(550, $4);
   1004   1.4   deraadt 				else if (!S_ISREG(stbuf.st_mode)) {
   1005   1.4   deraadt 					reply(550, "%s: not a plain file.", $4);
   1006   1.1       cgd 				} else {
   1007   1.4   deraadt 					struct tm *t;
   1008   1.1       cgd 					t = gmtime(&stbuf.st_mtime);
   1009   1.1       cgd 					reply(213,
   1010   1.7       jtc 					    "%04d%02d%02d%02d%02d%02d",
   1011  1.18     lukem 					    TM_YEAR_BASE + t->tm_year,
   1012   1.7       jtc 					    t->tm_mon+1, t->tm_mday,
   1013   1.1       cgd 					    t->tm_hour, t->tm_min, t->tm_sec);
   1014   1.1       cgd 				}
   1015   1.1       cgd 			}
   1016   1.1       cgd 			if ($4 != NULL)
   1017   1.4   deraadt 				free($4);
   1018   1.1       cgd 		}
   1019  1.23     lukem 
   1020   1.4   deraadt 	| error CRLF
   1021   1.4   deraadt 		{
   1022   1.1       cgd 			yyerrok;
   1023   1.1       cgd 		}
   1024   1.1       cgd 	;
   1025  1.21     lukem 
   1026   1.4   deraadt rcmd
   1027  1.42     lukem 	: REST check_login SP byte_size CRLF
   1028  1.23     lukem 		{
   1029  1.42     lukem 			if ($2) {
   1030  1.42     lukem 				fromname = NULL;
   1031  1.42     lukem 				restart_point = $4; /* XXX $3 is only "int" */
   1032  1.42     lukem 				reply(350, "Restarting at %qd. %s",
   1033  1.42     lukem 				    (qdfmt_t)restart_point,
   1034  1.23     lukem 			    "Send STORE or RETRIEVE to initiate transfer.");
   1035  1.42     lukem 			}
   1036  1.23     lukem 		}
   1037  1.42     lukem 
   1038  1.23     lukem 	| RNFR check_modify SP pathname CRLF
   1039   1.4   deraadt 		{
   1040   1.1       cgd 			restart_point = (off_t) 0;
   1041   1.1       cgd 			if ($2 && $4) {
   1042   1.4   deraadt 				fromname = renamefrom($4);
   1043   1.1       cgd 			}
   1044  1.42     lukem 			if ($4)
   1045  1.42     lukem 				free($4);
   1046   1.1       cgd 		}
   1047   1.1       cgd 	;
   1048   1.4   deraadt 
   1049   1.4   deraadt username
   1050   1.4   deraadt 	: STRING
   1051   1.1       cgd 	;
   1052   1.1       cgd 
   1053   1.4   deraadt password
   1054   1.4   deraadt 	: /* empty */
   1055   1.4   deraadt 		{
   1056   1.4   deraadt 			$$ = (char *)calloc(1, sizeof(char));
   1057   1.1       cgd 		}
   1058  1.23     lukem 
   1059   1.4   deraadt 	| STRING
   1060   1.1       cgd 	;
   1061   1.1       cgd 
   1062   1.4   deraadt byte_size
   1063   1.4   deraadt 	: NUMBER
   1064   1.1       cgd 	;
   1065   1.1       cgd 
   1066   1.4   deraadt host_port
   1067   1.4   deraadt 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1068   1.1       cgd 		NUMBER COMMA NUMBER
   1069   1.4   deraadt 		{
   1070   1.4   deraadt 			char *a, *p;
   1071   1.1       cgd 
   1072  1.32    itojun 			data_dest.su_len = sizeof(struct sockaddr_in);
   1073  1.32    itojun 			data_dest.su_family = AF_INET;
   1074  1.32    itojun 			p = (char *)&data_dest.su_sin.sin_port;
   1075   1.6   mycroft 			p[0] = $9; p[1] = $11;
   1076  1.32    itojun 			a = (char *)&data_dest.su_sin.sin_addr;
   1077   1.1       cgd 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
   1078   1.1       cgd 		}
   1079   1.1       cgd 	;
   1080   1.1       cgd 
   1081  1.34    itojun host_long_port4
   1082  1.34    itojun 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1083  1.34    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1084  1.34    itojun 		NUMBER
   1085  1.34    itojun 		{
   1086  1.34    itojun 			char *a, *p;
   1087  1.34    itojun 
   1088  1.34    itojun 			data_dest.su_sin.sin_len =
   1089  1.34    itojun 				sizeof(struct sockaddr_in);
   1090  1.34    itojun 			data_dest.su_family = AF_INET;
   1091  1.34    itojun 			p = (char *)&data_dest.su_port;
   1092  1.34    itojun 			p[0] = $15; p[1] = $17;
   1093  1.34    itojun 			a = (char *)&data_dest.su_sin.sin_addr;
   1094  1.34    itojun 			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
   1095  1.35    itojun 
   1096  1.35    itojun 			/* reject invalid LPRT command */
   1097  1.35    itojun 			if ($1 != 4 || $3 != 4 || $13 != 2)
   1098  1.35    itojun 				memset(&data_dest, 0, sizeof(data_dest));
   1099  1.34    itojun 		}
   1100  1.34    itojun 	;
   1101  1.34    itojun 
   1102  1.34    itojun host_long_port6
   1103  1.32    itojun 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1104  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1105  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1106  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1107  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1108  1.32    itojun 		NUMBER
   1109  1.32    itojun 		{
   1110  1.32    itojun 			char *a, *p;
   1111  1.32    itojun 
   1112  1.32    itojun 			data_dest.su_sin6.sin6_len =
   1113  1.32    itojun 				sizeof(struct sockaddr_in6);
   1114  1.32    itojun 			data_dest.su_family = AF_INET6;
   1115  1.32    itojun 			p = (char *)&data_dest.su_port;
   1116  1.32    itojun 			p[0] = $39; p[1] = $41;
   1117  1.32    itojun 			a = (char *)&data_dest.su_sin6.sin6_addr;
   1118  1.32    itojun 			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
   1119  1.32    itojun 			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
   1120  1.32    itojun 			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
   1121  1.32    itojun 			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
   1122  1.37    itojun 			if (his_addr.su_family == AF_INET6) {
   1123  1.37    itojun 				/* XXX more sanity checks! */
   1124  1.37    itojun 				data_dest.su_sin6.sin6_scope_id =
   1125  1.37    itojun 					his_addr.su_sin6.sin6_scope_id;
   1126  1.37    itojun 			}
   1127  1.35    itojun 
   1128  1.35    itojun 			/* reject invalid LPRT command */
   1129  1.35    itojun 			if ($1 != 6 || $3 != 16 || $37 != 2)
   1130  1.35    itojun 				memset(&data_dest, 0, sizeof(data_dest));
   1131  1.32    itojun 		}
   1132  1.32    itojun 	;
   1133  1.32    itojun 
   1134   1.4   deraadt form_code
   1135   1.4   deraadt 	: N
   1136   1.4   deraadt 		{
   1137   1.4   deraadt 			$$ = FORM_N;
   1138   1.4   deraadt 		}
   1139  1.23     lukem 
   1140   1.4   deraadt 	| T
   1141   1.4   deraadt 		{
   1142   1.4   deraadt 			$$ = FORM_T;
   1143   1.4   deraadt 		}
   1144  1.23     lukem 
   1145   1.4   deraadt 	| C
   1146   1.4   deraadt 		{
   1147   1.4   deraadt 			$$ = FORM_C;
   1148   1.4   deraadt 		}
   1149   1.1       cgd 	;
   1150   1.1       cgd 
   1151   1.4   deraadt type_code
   1152   1.4   deraadt 	: A
   1153   1.4   deraadt 		{
   1154   1.4   deraadt 			cmd_type = TYPE_A;
   1155   1.4   deraadt 			cmd_form = FORM_N;
   1156   1.4   deraadt 		}
   1157  1.23     lukem 
   1158   1.4   deraadt 	| A SP form_code
   1159   1.4   deraadt 		{
   1160   1.4   deraadt 			cmd_type = TYPE_A;
   1161   1.4   deraadt 			cmd_form = $3;
   1162   1.4   deraadt 		}
   1163  1.23     lukem 
   1164   1.4   deraadt 	| E
   1165   1.4   deraadt 		{
   1166   1.4   deraadt 			cmd_type = TYPE_E;
   1167   1.4   deraadt 			cmd_form = FORM_N;
   1168   1.4   deraadt 		}
   1169  1.23     lukem 
   1170   1.4   deraadt 	| E SP form_code
   1171   1.4   deraadt 		{
   1172   1.4   deraadt 			cmd_type = TYPE_E;
   1173   1.4   deraadt 			cmd_form = $3;
   1174   1.4   deraadt 		}
   1175  1.23     lukem 
   1176   1.4   deraadt 	| I
   1177   1.4   deraadt 		{
   1178   1.4   deraadt 			cmd_type = TYPE_I;
   1179   1.4   deraadt 		}
   1180  1.23     lukem 
   1181   1.4   deraadt 	| L
   1182   1.4   deraadt 		{
   1183   1.4   deraadt 			cmd_type = TYPE_L;
   1184   1.4   deraadt 			cmd_bytesz = NBBY;
   1185   1.4   deraadt 		}
   1186  1.23     lukem 
   1187   1.4   deraadt 	| L SP byte_size
   1188   1.4   deraadt 		{
   1189   1.4   deraadt 			cmd_type = TYPE_L;
   1190   1.4   deraadt 			cmd_bytesz = $3;
   1191   1.4   deraadt 		}
   1192  1.23     lukem 
   1193   1.4   deraadt 		/* this is for a bug in the BBN ftp */
   1194   1.4   deraadt 	| L byte_size
   1195   1.4   deraadt 		{
   1196   1.4   deraadt 			cmd_type = TYPE_L;
   1197   1.4   deraadt 			cmd_bytesz = $2;
   1198   1.4   deraadt 		}
   1199   1.1       cgd 	;
   1200   1.1       cgd 
   1201   1.4   deraadt struct_code
   1202   1.4   deraadt 	: F
   1203   1.4   deraadt 		{
   1204   1.4   deraadt 			$$ = STRU_F;
   1205   1.4   deraadt 		}
   1206  1.23     lukem 
   1207   1.4   deraadt 	| R
   1208   1.4   deraadt 		{
   1209   1.4   deraadt 			$$ = STRU_R;
   1210   1.4   deraadt 		}
   1211  1.23     lukem 
   1212   1.4   deraadt 	| P
   1213   1.4   deraadt 		{
   1214   1.4   deraadt 			$$ = STRU_P;
   1215   1.4   deraadt 		}
   1216   1.1       cgd 	;
   1217   1.1       cgd 
   1218   1.4   deraadt mode_code
   1219   1.4   deraadt 	: S
   1220   1.4   deraadt 		{
   1221   1.4   deraadt 			$$ = MODE_S;
   1222   1.4   deraadt 		}
   1223  1.23     lukem 
   1224   1.4   deraadt 	| B
   1225   1.4   deraadt 		{
   1226   1.4   deraadt 			$$ = MODE_B;
   1227   1.4   deraadt 		}
   1228  1.23     lukem 
   1229   1.4   deraadt 	| C
   1230   1.4   deraadt 		{
   1231   1.4   deraadt 			$$ = MODE_C;
   1232   1.4   deraadt 		}
   1233   1.1       cgd 	;
   1234   1.1       cgd 
   1235   1.4   deraadt pathname
   1236   1.4   deraadt 	: pathstring
   1237   1.4   deraadt 		{
   1238   1.4   deraadt 			/*
   1239   1.4   deraadt 			 * Problem: this production is used for all pathname
   1240   1.4   deraadt 			 * processing, but only gives a 550 error reply.
   1241   1.9     lukem 			 * This is a valid reply in some cases but not in
   1242   1.9     lukem 			 * others.
   1243   1.4   deraadt 			 */
   1244   1.4   deraadt 			if (logged_in && $1 && *$1 == '~') {
   1245   1.4   deraadt 				glob_t gl;
   1246   1.4   deraadt 				int flags =
   1247  1.19    kleink 				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
   1248   1.4   deraadt 
   1249   1.9     lukem 				if ($1[1] == '\0')
   1250  1.23     lukem 					$$ = xstrdup(pw->pw_dir);
   1251   1.9     lukem 				else {
   1252   1.9     lukem 					memset(&gl, 0, sizeof(gl));
   1253   1.9     lukem 					if (glob($1, flags, NULL, &gl) ||
   1254   1.9     lukem 					    gl.gl_pathc == 0) {
   1255   1.9     lukem 						reply(550, "not found");
   1256   1.9     lukem 						$$ = NULL;
   1257   1.9     lukem 					} else
   1258  1.23     lukem 						$$ = xstrdup(gl.gl_pathv[0]);
   1259   1.9     lukem 					globfree(&gl);
   1260   1.4   deraadt 				}
   1261   1.4   deraadt 				free($1);
   1262   1.4   deraadt 			} else
   1263   1.4   deraadt 				$$ = $1;
   1264   1.4   deraadt 		}
   1265   1.1       cgd 	;
   1266   1.1       cgd 
   1267   1.4   deraadt pathstring
   1268   1.4   deraadt 	: STRING
   1269   1.1       cgd 	;
   1270   1.1       cgd 
   1271   1.4   deraadt octal_number
   1272   1.4   deraadt 	: NUMBER
   1273   1.4   deraadt 		{
   1274   1.4   deraadt 			int ret, dec, multby, digit;
   1275   1.1       cgd 
   1276   1.4   deraadt 			/*
   1277   1.4   deraadt 			 * Convert a number that was read as decimal number
   1278   1.4   deraadt 			 * to what it would be if it had been read as octal.
   1279   1.4   deraadt 			 */
   1280   1.4   deraadt 			dec = $1;
   1281   1.4   deraadt 			multby = 1;
   1282   1.4   deraadt 			ret = 0;
   1283   1.4   deraadt 			while (dec) {
   1284   1.4   deraadt 				digit = dec%10;
   1285   1.4   deraadt 				if (digit > 7) {
   1286   1.4   deraadt 					ret = -1;
   1287   1.4   deraadt 					break;
   1288   1.4   deraadt 				}
   1289   1.4   deraadt 				ret += digit * multby;
   1290   1.4   deraadt 				multby *= 8;
   1291   1.4   deraadt 				dec /= 10;
   1292   1.1       cgd 			}
   1293   1.4   deraadt 			$$ = ret;
   1294   1.1       cgd 		}
   1295   1.1       cgd 	;
   1296   1.1       cgd 
   1297  1.25     lukem mechanism_name
   1298  1.25     lukem 	: STRING
   1299  1.25     lukem 	;
   1300  1.25     lukem 
   1301  1.25     lukem base64data
   1302  1.25     lukem 	: STRING
   1303  1.25     lukem 	;
   1304  1.25     lukem 
   1305  1.25     lukem prot_code
   1306  1.25     lukem 	: STRING
   1307  1.25     lukem 	;
   1308  1.25     lukem 
   1309  1.25     lukem decimal_integer
   1310  1.25     lukem 	: NUMBER
   1311  1.25     lukem 	;
   1312  1.25     lukem 
   1313   1.4   deraadt check_login
   1314   1.4   deraadt 	: /* empty */
   1315   1.4   deraadt 		{
   1316   1.4   deraadt 			if (logged_in)
   1317   1.4   deraadt 				$$ = 1;
   1318   1.4   deraadt 			else {
   1319   1.4   deraadt 				reply(530, "Please login with USER and PASS.");
   1320   1.4   deraadt 				$$ = 0;
   1321  1.22     lukem 				hasyyerrored = 1;
   1322   1.4   deraadt 			}
   1323   1.1       cgd 		}
   1324   1.1       cgd 	;
   1325  1.21     lukem 
   1326  1.12     lukem check_modify
   1327   1.8       cjs 	: /* empty */
   1328   1.8       cjs 		{
   1329  1.22     lukem 			if (logged_in) {
   1330  1.22     lukem 				if (curclass.modify)
   1331  1.12     lukem 					$$ = 1;
   1332  1.22     lukem 				else {
   1333   1.8       cjs 					reply(502,
   1334  1.12     lukem 					"No permission to use this command.");
   1335   1.8       cjs 					$$ = 0;
   1336  1.22     lukem 					hasyyerrored = 1;
   1337  1.14   hannken 				}
   1338   1.8       cjs 			} else {
   1339   1.8       cjs 				reply(530, "Please login with USER and PASS.");
   1340   1.8       cjs 				$$ = 0;
   1341  1.22     lukem 				hasyyerrored = 1;
   1342   1.8       cjs 			}
   1343   1.8       cjs 		}
   1344   1.1       cgd 
   1345  1.41     lukem check_upload
   1346  1.41     lukem 	: /* empty */
   1347  1.41     lukem 		{
   1348  1.41     lukem 			if (logged_in) {
   1349  1.41     lukem 				if (curclass.upload)
   1350  1.41     lukem 					$$ = 1;
   1351  1.41     lukem 				else {
   1352  1.41     lukem 					reply(502,
   1353  1.41     lukem 					"No permission to use this command.");
   1354  1.41     lukem 					$$ = 0;
   1355  1.41     lukem 					hasyyerrored = 1;
   1356  1.41     lukem 				}
   1357  1.41     lukem 			} else {
   1358  1.41     lukem 				reply(530, "Please login with USER and PASS.");
   1359  1.41     lukem 				$$ = 0;
   1360  1.41     lukem 				hasyyerrored = 1;
   1361  1.41     lukem 			}
   1362  1.41     lukem 		}
   1363  1.41     lukem 
   1364  1.41     lukem 
   1365   1.1       cgd %%
   1366   1.1       cgd 
   1367   1.1       cgd #define	CMD	0	/* beginning of command */
   1368   1.1       cgd #define	ARGS	1	/* expect miscellaneous arguments */
   1369   1.1       cgd #define	STR1	2	/* expect SP followed by STRING */
   1370   1.1       cgd #define	STR2	3	/* expect STRING */
   1371   1.1       cgd #define	OSTR	4	/* optional SP then STRING */
   1372   1.1       cgd #define	ZSTR1	5	/* SP then optional STRING */
   1373   1.1       cgd #define	ZSTR2	6	/* optional STRING after SP */
   1374   1.1       cgd #define	SITECMD	7	/* SITE command */
   1375   1.1       cgd #define	NSTR	8	/* Number followed by a string */
   1376  1.21     lukem #define NOARGS	9	/* No arguments allowed */
   1377   1.1       cgd 
   1378   1.1       cgd struct tab {
   1379   1.1       cgd 	char	*name;
   1380  1.23     lukem 	short	 token;
   1381  1.23     lukem 	short	 state;
   1382  1.23     lukem 	short	 implemented;	/* 1 if command is implemented */
   1383  1.23     lukem 	short	 hasopts;	/* 1 if command takes options */
   1384   1.1       cgd 	char	*help;
   1385  1.23     lukem 	char	*options;
   1386   1.1       cgd };
   1387   1.1       cgd 
   1388  1.21     lukem struct tab cmdtab[] = {
   1389  1.21     lukem 				/* From RFC 959, in order defined (5.3.1) */
   1390  1.23     lukem 	{ "USER", USER, STR1,	1, 0,	"<sp> username" },
   1391  1.23     lukem 	{ "PASS", PASS, ZSTR1,	1, 0,	"<sp> password" },
   1392  1.23     lukem 	{ "ACCT", ACCT, STR1,	0, 0,	"(specify account)" },
   1393  1.23     lukem 	{ "CWD",  CWD,  OSTR,	1, 0,	"[ <sp> directory-name ]" },
   1394  1.23     lukem 	{ "CDUP", CDUP, NOARGS,	1, 0,	"(change to parent directory)" },
   1395  1.23     lukem 	{ "SMNT", SMNT, ARGS,	0, 0,	"(structure mount)" },
   1396  1.38    simonb 	{ "QUIT", QUIT, NOARGS,	1, 0,	"(terminate service)" },
   1397  1.23     lukem 	{ "REIN", REIN, NOARGS,	0, 0,	"(reinitialize server state)" },
   1398  1.23     lukem 	{ "PORT", PORT, ARGS,	1, 0,	"<sp> b0, b1, b2, b3, b4" },
   1399  1.32    itojun 	{ "LPRT", LPRT, ARGS,	1, 0,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
   1400  1.32    itojun 	{ "EPRT", EPRT, STR1,	1, 0,	"<sp> |af|addr|port|" },
   1401  1.23     lukem 	{ "PASV", PASV, NOARGS,	1, 0,	"(set server in passive mode)" },
   1402  1.32    itojun 	{ "LPSV", LPSV, ARGS,	1, 0,	"(set server in passive mode)" },
   1403  1.32    itojun 	{ "EPSV", EPSV, ARGS,	1, 0,	"[<sp> af|ALL]" },
   1404  1.23     lukem 	{ "TYPE", TYPE, ARGS,	1, 0,	"<sp> [ A | E | I | L ]" },
   1405  1.23     lukem 	{ "STRU", STRU, ARGS,	1, 0,	"(specify file structure)" },
   1406  1.23     lukem 	{ "MODE", MODE, ARGS,	1, 0,	"(specify transfer mode)" },
   1407  1.23     lukem 	{ "RETR", RETR, STR1,	1, 0,	"<sp> file-name" },
   1408  1.23     lukem 	{ "STOR", STOR, STR1,	1, 0,	"<sp> file-name" },
   1409  1.23     lukem 	{ "STOU", STOU, STR1,	1, 0,	"<sp> file-name" },
   1410  1.23     lukem 	{ "APPE", APPE, STR1,	1, 0,	"<sp> file-name" },
   1411  1.23     lukem 	{ "ALLO", ALLO, ARGS,	1, 0,	"allocate storage (vacuously)" },
   1412  1.23     lukem 	{ "REST", REST, ARGS,	1, 0,	"<sp> offset (restart command)" },
   1413  1.23     lukem 	{ "RNFR", RNFR, STR1,	1, 0,	"<sp> file-name" },
   1414  1.23     lukem 	{ "RNTO", RNTO, STR1,	1, 0,	"<sp> file-name" },
   1415  1.23     lukem 	{ "ABOR", ABOR, NOARGS,	1, 0,	"(abort operation)" },
   1416  1.23     lukem 	{ "DELE", DELE, STR1,	1, 0,	"<sp> file-name" },
   1417  1.23     lukem 	{ "RMD",  RMD,  STR1,	1, 0,	"<sp> path-name" },
   1418  1.23     lukem 	{ "MKD",  MKD,  STR1,	1, 0,	"<sp> path-name" },
   1419  1.23     lukem 	{ "PWD",  PWD,  NOARGS,	1, 0,	"(return current directory)" },
   1420  1.23     lukem 	{ "LIST", LIST, OSTR,	1, 0,	"[ <sp> path-name ]" },
   1421  1.23     lukem 	{ "NLST", NLST, OSTR,	1, 0,	"[ <sp> path-name ]" },
   1422  1.23     lukem 	{ "SITE", SITE, SITECMD, 1, 0,	"site-cmd [ <sp> arguments ]" },
   1423  1.23     lukem 	{ "SYST", SYST, NOARGS,	1, 0,	"(get type of operating system)" },
   1424  1.23     lukem 	{ "STAT", STAT, OSTR,	1, 0,	"[ <sp> path-name ]" },
   1425  1.23     lukem 	{ "HELP", HELP, OSTR,	1, 0,	"[ <sp> <string> ]" },
   1426  1.23     lukem 	{ "NOOP", NOOP, NOARGS,	1, 1,	"" },
   1427  1.23     lukem 
   1428  1.25     lukem 				/* From RFC 2228, in order defined */
   1429  1.25     lukem 	{ "AUTH", AUTH, STR1,	1, 0,	"<sp> mechanism-name" },
   1430  1.25     lukem 	{ "ADAT", ADAT, STR1,	1, 0,	"<sp> base-64-data" },
   1431  1.25     lukem 	{ "PROT", PROT, STR1,	1, 0,	"<sp> prot-code" },
   1432  1.25     lukem 	{ "PBSZ", PBSZ, ARGS,	1, 0,	"<sp> decimal-integer" },
   1433  1.25     lukem 	{ "CCC",  CCC,  NOARGS,	1, 0,	"(Disable data protection)" },
   1434  1.25     lukem 	{ "MIC",  MIC,  STR1,	1, 0,	"<sp> base64data" },
   1435  1.25     lukem 	{ "CONF", CONF, STR1,	1, 0,	"<sp> base64data" },
   1436  1.25     lukem 	{ "ENC",  ENC,  STR1,	1, 0,	"<sp> base64data" },
   1437  1.25     lukem 
   1438  1.25     lukem 				/* From RFC 2389, in order defined */
   1439  1.23     lukem 	{ "FEAT", FEAT, NOARGS,	1, 0,	"(display extended features)" },
   1440  1.23     lukem 	{ "OPTS", OPTS, STR1,	1, 0,	"<sp> command [ <sp> options ]" },
   1441  1.23     lukem 
   1442  1.23     lukem 				/* Non standardized extensions */
   1443  1.23     lukem 	{ "SIZE", SIZE, OSTR,	1, 0,	"<sp> path-name" },
   1444  1.23     lukem 	{ "MDTM", MDTM, OSTR,	1, 0,	"<sp> path-name" },
   1445  1.21     lukem 
   1446  1.21     lukem 				/* obsolete commands */
   1447  1.23     lukem 	{ "MAIL", MAIL, OSTR,	0, 0,	"(mail to user)" },
   1448  1.23     lukem 	{ "MLFL", MLFL, OSTR,	0, 0,	"(mail file)" },
   1449  1.23     lukem 	{ "MRCP", MRCP, STR1,	0, 0,	"(mail recipient)" },
   1450  1.23     lukem 	{ "MRSQ", MRSQ, OSTR,	0, 0,	"(mail recipient scheme question)" },
   1451  1.23     lukem 	{ "MSAM", MSAM, OSTR,	0, 0,	"(mail send to terminal and mailbox)" },
   1452  1.23     lukem 	{ "MSND", MSND, OSTR,	0, 0,	"(mail send to terminal)" },
   1453  1.23     lukem 	{ "MSOM", MSOM, OSTR,	0, 0,	"(mail send to terminal or mailbox)" },
   1454  1.23     lukem 	{ "XCUP", CDUP, NOARGS,	1, 0,	"(change to parent directory)" },
   1455  1.38    simonb 	{ "XCWD", CWD,  OSTR,	1, 0,	"[ <sp> directory-name ]" },
   1456  1.23     lukem 	{ "XMKD", MKD,  STR1,	1, 0,	"<sp> path-name" },
   1457  1.23     lukem 	{ "XPWD", PWD,  NOARGS,	1, 0,	"(return current directory)" },
   1458  1.23     lukem 	{ "XRMD", RMD,  STR1,	1, 0,	"<sp> path-name" },
   1459  1.21     lukem 
   1460  1.23     lukem 	{ NULL,   0,    0,	0, 0,	0 }
   1461   1.1       cgd };
   1462   1.1       cgd 
   1463   1.1       cgd struct tab sitetab[] = {
   1464  1.23     lukem 	{ "CHMOD", CHMOD, NSTR,	1, 0,	"<sp> mode <sp> file-name" },
   1465  1.23     lukem 	{ "HELP",  HELP,  OSTR,	1, 0,	"[ <sp> <string> ]" },
   1466  1.41     lukem 	{ "IDLE",  IDLE,  ARGS,	1, 0,	"[ <sp> maximum-idle-time ]" },
   1467  1.41     lukem 	{ "RATEGET", RATEGET, OSTR, 1,0,"[ <sp> get-throttle-rate ]" },
   1468  1.41     lukem 	{ "RATEPUT", RATEPUT, OSTR, 1,0,"[ <sp> put-throttle-rate ]" },
   1469  1.41     lukem 	{ "UMASK", UMASK, ARGS,	1, 0,	"[ <sp> umask ]" },
   1470  1.23     lukem 	{ NULL,    0,     0,	0, 0,	0 }
   1471   1.1       cgd };
   1472   1.1       cgd 
   1473  1.23     lukem static	void	 	help __P((struct tab *, char *));
   1474  1.23     lukem static	struct tab     *lookup __P((struct tab *, const char *));
   1475  1.23     lukem static	void		opts __P((const char *));
   1476  1.23     lukem static	void		sizecmd __P((char *));
   1477  1.23     lukem static	void		toolong __P((int));
   1478  1.23     lukem static	int		yylex __P((void));
   1479   1.4   deraadt 
   1480  1.32    itojun extern int epsvall;
   1481  1.32    itojun 
   1482   1.4   deraadt static struct tab *
   1483   1.1       cgd lookup(p, cmd)
   1484   1.4   deraadt 	struct tab *p;
   1485  1.23     lukem 	const char *cmd;
   1486   1.1       cgd {
   1487   1.1       cgd 
   1488   1.1       cgd 	for (; p->name != NULL; p++)
   1489  1.23     lukem 		if (strcasecmp(cmd, p->name) == 0)
   1490   1.1       cgd 			return (p);
   1491   1.1       cgd 	return (0);
   1492   1.1       cgd }
   1493   1.1       cgd 
   1494   1.1       cgd #include <arpa/telnet.h>
   1495   1.1       cgd 
   1496   1.1       cgd /*
   1497   1.1       cgd  * getline - a hacked up version of fgets to ignore TELNET escape codes.
   1498   1.1       cgd  */
   1499   1.1       cgd char *
   1500   1.1       cgd getline(s, n, iop)
   1501   1.1       cgd 	char *s;
   1502   1.4   deraadt 	int n;
   1503   1.4   deraadt 	FILE *iop;
   1504   1.1       cgd {
   1505  1.27     lukem 	off_t b;
   1506   1.4   deraadt 	int c;
   1507  1.21     lukem 	char *cs;
   1508   1.1       cgd 
   1509   1.1       cgd 	cs = s;
   1510   1.1       cgd /* tmpline may contain saved command from urgent mode interruption */
   1511   1.1       cgd 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
   1512   1.1       cgd 		*cs++ = tmpline[c];
   1513   1.1       cgd 		if (tmpline[c] == '\n') {
   1514   1.1       cgd 			*cs++ = '\0';
   1515   1.1       cgd 			if (debug)
   1516   1.1       cgd 				syslog(LOG_DEBUG, "command: %s", s);
   1517   1.1       cgd 			tmpline[0] = '\0';
   1518   1.1       cgd 			return(s);
   1519   1.1       cgd 		}
   1520   1.1       cgd 		if (c == 0)
   1521   1.1       cgd 			tmpline[0] = '\0';
   1522   1.1       cgd 	}
   1523   1.1       cgd 	while ((c = getc(iop)) != EOF) {
   1524  1.27     lukem 		total_bytes++;
   1525  1.27     lukem 		total_bytes_in++;
   1526   1.1       cgd 		c &= 0377;
   1527   1.1       cgd 		if (c == IAC) {
   1528   1.1       cgd 		    if ((c = getc(iop)) != EOF) {
   1529  1.27     lukem 			total_bytes++;
   1530  1.27     lukem 			total_bytes_in++;
   1531   1.1       cgd 			c &= 0377;
   1532   1.1       cgd 			switch (c) {
   1533   1.1       cgd 			case WILL:
   1534   1.1       cgd 			case WONT:
   1535   1.1       cgd 				c = getc(iop);
   1536  1.27     lukem 				total_bytes++;
   1537  1.27     lukem 				total_bytes_in++;
   1538  1.27     lukem 				b = printf("%c%c%c", IAC, DONT, 0377&c);
   1539  1.27     lukem 				total_bytes += b;
   1540  1.27     lukem 				total_bytes_out += b;
   1541   1.1       cgd 				(void) fflush(stdout);
   1542   1.1       cgd 				continue;
   1543   1.1       cgd 			case DO:
   1544   1.1       cgd 			case DONT:
   1545   1.1       cgd 				c = getc(iop);
   1546  1.27     lukem 				total_bytes++;
   1547  1.27     lukem 				total_bytes_in++;
   1548  1.27     lukem 				b = printf("%c%c%c", IAC, WONT, 0377&c);
   1549  1.27     lukem 				total_bytes += b;
   1550  1.27     lukem 				total_bytes_out += b;
   1551   1.1       cgd 				(void) fflush(stdout);
   1552   1.1       cgd 				continue;
   1553   1.1       cgd 			case IAC:
   1554   1.1       cgd 				break;
   1555   1.1       cgd 			default:
   1556   1.1       cgd 				continue;	/* ignore command */
   1557   1.1       cgd 			}
   1558   1.1       cgd 		    }
   1559   1.1       cgd 		}
   1560   1.1       cgd 		*cs++ = c;
   1561   1.1       cgd 		if (--n <= 0 || c == '\n')
   1562   1.1       cgd 			break;
   1563   1.1       cgd 	}
   1564   1.1       cgd 	if (c == EOF && cs == s)
   1565   1.1       cgd 		return (NULL);
   1566   1.1       cgd 	*cs++ = '\0';
   1567   1.4   deraadt 	if (debug) {
   1568  1.41     lukem 		if (curclass.type != CLASS_GUEST &&
   1569  1.41     lukem 		    strncasecmp("pass ", s, 5) == 0) {
   1570   1.4   deraadt 			/* Don't syslog passwords */
   1571   1.4   deraadt 			syslog(LOG_DEBUG, "command: %.5s ???", s);
   1572   1.4   deraadt 		} else {
   1573  1.21     lukem 			char *cp;
   1574  1.21     lukem 			int len;
   1575   1.4   deraadt 
   1576   1.4   deraadt 			/* Don't syslog trailing CR-LF */
   1577   1.4   deraadt 			len = strlen(s);
   1578   1.4   deraadt 			cp = s + len - 1;
   1579   1.4   deraadt 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
   1580   1.4   deraadt 				--cp;
   1581   1.4   deraadt 				--len;
   1582   1.4   deraadt 			}
   1583   1.4   deraadt 			syslog(LOG_DEBUG, "command: %.*s", len, s);
   1584   1.4   deraadt 		}
   1585   1.4   deraadt 	}
   1586   1.1       cgd 	return (s);
   1587   1.1       cgd }
   1588   1.1       cgd 
   1589   1.1       cgd static void
   1590   1.4   deraadt toolong(signo)
   1591   1.4   deraadt 	int signo;
   1592   1.1       cgd {
   1593   1.1       cgd 
   1594   1.1       cgd 	reply(421,
   1595  1.12     lukem 	    "Timeout (%d seconds): closing control connection.",
   1596  1.12     lukem 	    curclass.timeout);
   1597   1.4   deraadt 	if (logging)
   1598   1.4   deraadt 		syslog(LOG_INFO, "User %s timed out after %d seconds",
   1599  1.41     lukem 		    (pw ? pw->pw_name : "unknown"), curclass.timeout);
   1600   1.1       cgd 	dologout(1);
   1601   1.1       cgd }
   1602   1.1       cgd 
   1603   1.4   deraadt static int
   1604   1.1       cgd yylex()
   1605   1.1       cgd {
   1606   1.1       cgd 	static int cpos, state;
   1607   1.4   deraadt 	char *cp, *cp2;
   1608   1.4   deraadt 	struct tab *p;
   1609  1.22     lukem 	int n;
   1610   1.4   deraadt 	char c;
   1611   1.1       cgd 
   1612  1.23     lukem 	switch (state) {
   1613   1.1       cgd 
   1614  1.23     lukem 	case CMD:
   1615  1.23     lukem 		hasyyerrored = 0;
   1616  1.23     lukem 		(void) signal(SIGALRM, toolong);
   1617  1.23     lukem 		(void) alarm(curclass.timeout);
   1618  1.23     lukem 		if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
   1619  1.23     lukem 			reply(221, "You could at least say goodbye.");
   1620  1.23     lukem 			dologout(0);
   1621  1.23     lukem 		}
   1622  1.23     lukem 		(void) alarm(0);
   1623  1.42     lukem 		if ((cp = strchr(cbuf, '\r'))) {
   1624  1.42     lukem 			*cp = '\0';
   1625   1.3       cgd #ifdef HASSETPROCTITLE
   1626  1.42     lukem 			if (strncasecmp(cbuf, "PASS", 4) != 0)
   1627  1.42     lukem 				setproctitle("%s: %s", proctitle, cbuf);
   1628   1.3       cgd #endif /* HASSETPROCTITLE */
   1629  1.23     lukem 			*cp++ = '\n';
   1630  1.23     lukem 			*cp = '\0';
   1631  1.23     lukem 		}
   1632  1.23     lukem 		if ((cp = strpbrk(cbuf, " \n")))
   1633  1.23     lukem 			cpos = cp - cbuf;
   1634  1.23     lukem 		if (cpos == 0)
   1635  1.23     lukem 			cpos = 4;
   1636  1.23     lukem 		c = cbuf[cpos];
   1637  1.23     lukem 		cbuf[cpos] = '\0';
   1638  1.23     lukem 		p = lookup(cmdtab, cbuf);
   1639  1.23     lukem 		cbuf[cpos] = c;
   1640  1.23     lukem 		if (p != NULL) {
   1641  1.23     lukem 			if (p->implemented == 0) {
   1642  1.23     lukem 				reply(502, "%s command not implemented.",
   1643  1.23     lukem 				    p->name);
   1644  1.23     lukem 				hasyyerrored = 1;
   1645  1.23     lukem 				break;
   1646  1.23     lukem 			}
   1647  1.23     lukem 			state = p->state;
   1648  1.23     lukem 			yylval.s = p->name;
   1649  1.23     lukem 			return (p->token);
   1650  1.23     lukem 		}
   1651  1.23     lukem 		break;
   1652  1.23     lukem 
   1653  1.23     lukem 	case SITECMD:
   1654  1.23     lukem 		if (cbuf[cpos] == ' ') {
   1655  1.23     lukem 			cpos++;
   1656  1.23     lukem 			return (SP);
   1657  1.23     lukem 		}
   1658  1.23     lukem 		cp = &cbuf[cpos];
   1659  1.23     lukem 		if ((cp2 = strpbrk(cp, " \n")))
   1660  1.23     lukem 			cpos = cp2 - cbuf;
   1661  1.23     lukem 		c = cbuf[cpos];
   1662  1.23     lukem 		cbuf[cpos] = '\0';
   1663  1.23     lukem 		p = lookup(sitetab, cp);
   1664  1.23     lukem 		cbuf[cpos] = c;
   1665  1.23     lukem 		if (p != NULL) {
   1666  1.23     lukem 			if (p->implemented == 0) {
   1667  1.23     lukem 				reply(502, "SITE %s command not implemented.",
   1668  1.23     lukem 				    p->name);
   1669  1.23     lukem 				hasyyerrored = 1;
   1670  1.23     lukem 				break;
   1671  1.23     lukem 			}
   1672  1.23     lukem 			state = p->state;
   1673  1.23     lukem 			yylval.s = p->name;
   1674  1.23     lukem 			return (p->token);
   1675  1.23     lukem 		}
   1676  1.23     lukem 		break;
   1677  1.23     lukem 
   1678  1.23     lukem 	case OSTR:
   1679  1.23     lukem 		if (cbuf[cpos] == '\n') {
   1680  1.23     lukem 			state = CMD;
   1681  1.23     lukem 			return (CRLF);
   1682  1.23     lukem 		}
   1683  1.23     lukem 		/* FALLTHROUGH */
   1684  1.23     lukem 
   1685  1.23     lukem 	case STR1:
   1686  1.23     lukem 	case ZSTR1:
   1687  1.23     lukem 	dostr1:
   1688  1.23     lukem 		if (cbuf[cpos] == ' ') {
   1689  1.23     lukem 			cpos++;
   1690  1.39      tron 			state = state == OSTR ? STR2 : state+1;
   1691  1.23     lukem 			return (SP);
   1692  1.23     lukem 		}
   1693  1.23     lukem 		break;
   1694  1.23     lukem 
   1695  1.23     lukem 	case ZSTR2:
   1696  1.23     lukem 		if (cbuf[cpos] == '\n') {
   1697  1.23     lukem 			state = CMD;
   1698  1.23     lukem 			return (CRLF);
   1699  1.23     lukem 		}
   1700  1.23     lukem 		/* FALLTHROUGH */
   1701  1.23     lukem 
   1702  1.23     lukem 	case STR2:
   1703  1.23     lukem 		cp = &cbuf[cpos];
   1704  1.23     lukem 		n = strlen(cp);
   1705  1.23     lukem 		cpos += n - 1;
   1706  1.23     lukem 		/*
   1707  1.23     lukem 		 * Make sure the string is nonempty and \n terminated.
   1708  1.23     lukem 		 */
   1709  1.23     lukem 		if (n > 1 && cbuf[cpos] == '\n') {
   1710  1.23     lukem 			cbuf[cpos] = '\0';
   1711  1.23     lukem 			yylval.s = xstrdup(cp);
   1712  1.23     lukem 			cbuf[cpos] = '\n';
   1713  1.23     lukem 			state = ARGS;
   1714  1.23     lukem 			return (STRING);
   1715  1.23     lukem 		}
   1716  1.23     lukem 		break;
   1717  1.23     lukem 
   1718  1.23     lukem 	case NSTR:
   1719  1.23     lukem 		if (cbuf[cpos] == ' ') {
   1720  1.23     lukem 			cpos++;
   1721  1.23     lukem 			return (SP);
   1722  1.23     lukem 		}
   1723  1.23     lukem 		if (isdigit(cbuf[cpos])) {
   1724  1.23     lukem 			cp = &cbuf[cpos];
   1725  1.23     lukem 			while (isdigit(cbuf[++cpos]))
   1726  1.23     lukem 				;
   1727   1.1       cgd 			c = cbuf[cpos];
   1728   1.1       cgd 			cbuf[cpos] = '\0';
   1729  1.23     lukem 			yylval.i = atoi(cp);
   1730   1.1       cgd 			cbuf[cpos] = c;
   1731  1.23     lukem 			state = STR1;
   1732  1.23     lukem 			return (NUMBER);
   1733  1.23     lukem 		}
   1734  1.23     lukem 		state = STR1;
   1735  1.23     lukem 		goto dostr1;
   1736   1.1       cgd 
   1737  1.23     lukem 	case ARGS:
   1738  1.23     lukem 		if (isdigit(cbuf[cpos])) {
   1739   1.1       cgd 			cp = &cbuf[cpos];
   1740  1.23     lukem 			while (isdigit(cbuf[++cpos]))
   1741  1.23     lukem 				;
   1742   1.1       cgd 			c = cbuf[cpos];
   1743   1.1       cgd 			cbuf[cpos] = '\0';
   1744  1.23     lukem 			yylval.i = atoi(cp);
   1745   1.1       cgd 			cbuf[cpos] = c;
   1746  1.23     lukem 			return (NUMBER);
   1747  1.32    itojun 		}
   1748  1.32    itojun 		if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
   1749  1.32    itojun 		 && !isalnum(cbuf[cpos + 3])) {
   1750  1.32    itojun 			yylval.s = xstrdup("ALL");
   1751  1.32    itojun 			cpos += 3;
   1752  1.32    itojun 			return ALL;
   1753  1.23     lukem 		}
   1754  1.23     lukem 		switch (cbuf[cpos++]) {
   1755  1.23     lukem 
   1756  1.23     lukem 		case '\n':
   1757   1.1       cgd 			state = CMD;
   1758  1.23     lukem 			return (CRLF);
   1759  1.23     lukem 
   1760  1.23     lukem 		case ' ':
   1761  1.23     lukem 			return (SP);
   1762  1.23     lukem 
   1763  1.23     lukem 		case ',':
   1764  1.23     lukem 			return (COMMA);
   1765  1.23     lukem 
   1766  1.23     lukem 		case 'A':
   1767  1.23     lukem 		case 'a':
   1768  1.23     lukem 			return (A);
   1769  1.23     lukem 
   1770  1.23     lukem 		case 'B':
   1771  1.23     lukem 		case 'b':
   1772  1.23     lukem 			return (B);
   1773  1.23     lukem 
   1774  1.23     lukem 		case 'C':
   1775  1.23     lukem 		case 'c':
   1776  1.23     lukem 			return (C);
   1777  1.23     lukem 
   1778  1.23     lukem 		case 'E':
   1779  1.23     lukem 		case 'e':
   1780  1.23     lukem 			return (E);
   1781  1.23     lukem 
   1782  1.23     lukem 		case 'F':
   1783  1.23     lukem 		case 'f':
   1784  1.23     lukem 			return (F);
   1785  1.23     lukem 
   1786  1.23     lukem 		case 'I':
   1787  1.23     lukem 		case 'i':
   1788  1.23     lukem 			return (I);
   1789   1.1       cgd 
   1790  1.23     lukem 		case 'L':
   1791  1.23     lukem 		case 'l':
   1792  1.23     lukem 			return (L);
   1793   1.1       cgd 
   1794  1.23     lukem 		case 'N':
   1795  1.23     lukem 		case 'n':
   1796  1.23     lukem 			return (N);
   1797   1.1       cgd 
   1798  1.23     lukem 		case 'P':
   1799  1.23     lukem 		case 'p':
   1800  1.23     lukem 			return (P);
   1801   1.1       cgd 
   1802  1.23     lukem 		case 'R':
   1803  1.23     lukem 		case 'r':
   1804  1.23     lukem 			return (R);
   1805   1.1       cgd 
   1806  1.23     lukem 		case 'S':
   1807  1.23     lukem 		case 's':
   1808  1.23     lukem 			return (S);
   1809   1.1       cgd 
   1810  1.23     lukem 		case 'T':
   1811  1.23     lukem 		case 't':
   1812  1.23     lukem 			return (T);
   1813   1.1       cgd 
   1814  1.23     lukem 		}
   1815  1.23     lukem 		break;
   1816  1.21     lukem 
   1817  1.23     lukem 	case NOARGS:
   1818  1.23     lukem 		if (cbuf[cpos] == '\n') {
   1819  1.23     lukem 			state = CMD;
   1820  1.23     lukem 			return (CRLF);
   1821   1.1       cgd 		}
   1822  1.23     lukem 		c = cbuf[cpos];
   1823  1.23     lukem 		cbuf[cpos] = '\0';
   1824  1.23     lukem 		reply(501, "'%s' command does not take any arguments.", cbuf);
   1825  1.23     lukem 		hasyyerrored = 1;
   1826  1.23     lukem 		cbuf[cpos] = c;
   1827  1.23     lukem 		break;
   1828  1.23     lukem 
   1829  1.23     lukem 	default:
   1830  1.23     lukem 		fatal("Unknown state in scanner.");
   1831   1.1       cgd 	}
   1832  1.23     lukem 	yyerror(NULL);
   1833  1.23     lukem 	state = CMD;
   1834  1.23     lukem 	longjmp(errcatch, 0);
   1835  1.23     lukem 	/* NOTREACHED */
   1836   1.1       cgd }
   1837   1.1       cgd 
   1838  1.22     lukem /* ARGSUSED */
   1839  1.22     lukem void
   1840  1.22     lukem yyerror(s)
   1841  1.22     lukem 	char *s;
   1842  1.22     lukem {
   1843  1.22     lukem 	char *cp;
   1844  1.22     lukem 
   1845  1.22     lukem 	if (hasyyerrored)
   1846  1.22     lukem 		return;
   1847  1.22     lukem 	if ((cp = strchr(cbuf,'\n')) != NULL)
   1848  1.22     lukem 		*cp = '\0';
   1849  1.22     lukem 	reply(500, "'%s': command not understood.", cbuf);
   1850  1.22     lukem 	hasyyerrored = 1;
   1851  1.22     lukem }
   1852  1.22     lukem 
   1853   1.4   deraadt static void
   1854   1.1       cgd help(ctab, s)
   1855   1.1       cgd 	struct tab *ctab;
   1856   1.1       cgd 	char *s;
   1857   1.1       cgd {
   1858   1.4   deraadt 	struct tab *c;
   1859   1.4   deraadt 	int width, NCMDS;
   1860  1.27     lukem 	off_t b;
   1861   1.1       cgd 	char *type;
   1862   1.1       cgd 
   1863   1.1       cgd 	if (ctab == sitetab)
   1864   1.1       cgd 		type = "SITE ";
   1865   1.1       cgd 	else
   1866   1.1       cgd 		type = "";
   1867   1.1       cgd 	width = 0, NCMDS = 0;
   1868   1.1       cgd 	for (c = ctab; c->name != NULL; c++) {
   1869   1.1       cgd 		int len = strlen(c->name);
   1870   1.1       cgd 
   1871   1.1       cgd 		if (len > width)
   1872   1.1       cgd 			width = len;
   1873   1.1       cgd 		NCMDS++;
   1874   1.1       cgd 	}
   1875   1.1       cgd 	width = (width + 8) &~ 7;
   1876   1.1       cgd 	if (s == 0) {
   1877   1.4   deraadt 		int i, j, w;
   1878   1.1       cgd 		int columns, lines;
   1879   1.1       cgd 
   1880  1.28     lukem 		lreply(214, "");
   1881  1.28     lukem 		lreply(0, "The following %scommands are recognized.", type);
   1882  1.27     lukem 		lreply(0, "(`-' = not implemented, `+' = supports options)");
   1883   1.1       cgd 		columns = 76 / width;
   1884   1.1       cgd 		if (columns == 0)
   1885   1.1       cgd 			columns = 1;
   1886   1.1       cgd 		lines = (NCMDS + columns - 1) / columns;
   1887   1.1       cgd 		for (i = 0; i < lines; i++) {
   1888  1.28     lukem 			b = printf("    ");
   1889  1.27     lukem 			total_bytes += b;
   1890  1.27     lukem 			total_bytes_out += b;
   1891   1.1       cgd 			for (j = 0; j < columns; j++) {
   1892   1.1       cgd 				c = ctab + j * lines + i;
   1893  1.27     lukem 				b = printf("%s", c->name);
   1894  1.27     lukem 				total_bytes += b;
   1895  1.27     lukem 				total_bytes_out += b;
   1896  1.23     lukem 				w = strlen(c->name);
   1897  1.23     lukem 				if (! c->implemented) {
   1898  1.24     lukem 					putchar('-');
   1899  1.28     lukem 					total_bytes++;
   1900  1.28     lukem 					total_bytes_out++;
   1901  1.23     lukem 					w++;
   1902  1.23     lukem 				}
   1903  1.23     lukem 				if (c->hasopts) {
   1904  1.23     lukem 					putchar('+');
   1905  1.28     lukem 					total_bytes++;
   1906  1.28     lukem 					total_bytes_out++;
   1907  1.23     lukem 					w++;
   1908  1.23     lukem 				}
   1909   1.1       cgd 				if (c + lines >= &ctab[NCMDS])
   1910   1.1       cgd 					break;
   1911   1.1       cgd 				while (w < width) {
   1912   1.1       cgd 					putchar(' ');
   1913  1.28     lukem 					total_bytes++;
   1914  1.28     lukem 					total_bytes_out++;
   1915   1.1       cgd 					w++;
   1916   1.1       cgd 				}
   1917   1.1       cgd 			}
   1918  1.27     lukem 			b = printf("\r\n");
   1919  1.27     lukem 			total_bytes += b;
   1920  1.27     lukem 			total_bytes_out += b;
   1921   1.1       cgd 		}
   1922   1.1       cgd 		(void) fflush(stdout);
   1923   1.1       cgd 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
   1924   1.1       cgd 		return;
   1925   1.1       cgd 	}
   1926   1.1       cgd 	c = lookup(ctab, s);
   1927   1.1       cgd 	if (c == (struct tab *)0) {
   1928   1.1       cgd 		reply(502, "Unknown command %s.", s);
   1929   1.1       cgd 		return;
   1930   1.1       cgd 	}
   1931   1.1       cgd 	if (c->implemented)
   1932   1.1       cgd 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
   1933   1.1       cgd 	else
   1934  1.23     lukem 		reply(214, "%s%-*s\t%s; not implemented.", type, width,
   1935   1.1       cgd 		    c->name, c->help);
   1936   1.1       cgd }
   1937   1.1       cgd 
   1938   1.4   deraadt static void
   1939   1.1       cgd sizecmd(filename)
   1940   1.4   deraadt 	char *filename;
   1941   1.1       cgd {
   1942   1.1       cgd 	switch (type) {
   1943   1.1       cgd 	case TYPE_L:
   1944   1.1       cgd 	case TYPE_I: {
   1945   1.1       cgd 		struct stat stbuf;
   1946   1.4   deraadt 		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
   1947   1.1       cgd 			reply(550, "%s: not a plain file.", filename);
   1948   1.1       cgd 		else
   1949  1.30      ross 			reply(213, "%qu", (qufmt_t)stbuf.st_size);
   1950   1.4   deraadt 		break; }
   1951   1.1       cgd 	case TYPE_A: {
   1952   1.1       cgd 		FILE *fin;
   1953   1.4   deraadt 		int c;
   1954   1.4   deraadt 		off_t count;
   1955   1.1       cgd 		struct stat stbuf;
   1956   1.1       cgd 		fin = fopen(filename, "r");
   1957   1.1       cgd 		if (fin == NULL) {
   1958   1.1       cgd 			perror_reply(550, filename);
   1959   1.1       cgd 			return;
   1960   1.1       cgd 		}
   1961   1.4   deraadt 		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
   1962   1.1       cgd 			reply(550, "%s: not a plain file.", filename);
   1963   1.1       cgd 			(void) fclose(fin);
   1964   1.1       cgd 			return;
   1965   1.1       cgd 		}
   1966   1.1       cgd 
   1967   1.1       cgd 		count = 0;
   1968   1.1       cgd 		while((c=getc(fin)) != EOF) {
   1969   1.1       cgd 			if (c == '\n')	/* will get expanded to \r\n */
   1970   1.1       cgd 				count++;
   1971   1.1       cgd 			count++;
   1972   1.1       cgd 		}
   1973   1.1       cgd 		(void) fclose(fin);
   1974   1.1       cgd 
   1975  1.30      ross 		reply(213, "%qd", (qdfmt_t)count);
   1976   1.4   deraadt 		break; }
   1977   1.1       cgd 	default:
   1978   1.1       cgd 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
   1979   1.1       cgd 	}
   1980   1.1       cgd }
   1981  1.22     lukem 
   1982  1.23     lukem static void
   1983  1.23     lukem opts(command)
   1984  1.23     lukem 	const char *command;
   1985  1.23     lukem {
   1986  1.23     lukem 	struct tab *c;
   1987  1.23     lukem 	char *ep;
   1988  1.23     lukem 
   1989  1.23     lukem 	if ((ep = strchr(command, ' ')) != NULL)
   1990  1.23     lukem 		*ep++ = '\0';
   1991  1.23     lukem 	c = lookup(cmdtab, command);
   1992  1.23     lukem 	if (c == NULL) {
   1993  1.23     lukem 		reply(502, "Unknown command %s.", command);
   1994  1.23     lukem 		return;
   1995  1.23     lukem 	}
   1996  1.23     lukem 	if (c->implemented == 0) {
   1997  1.23     lukem 		reply(502, "%s command not implemented.", c->name);
   1998  1.23     lukem 		return;
   1999  1.23     lukem 	}
   2000  1.23     lukem 	if (c->hasopts == 0) {
   2001  1.23     lukem 		reply(501, "%s command does not support persistent options.",
   2002  1.23     lukem 		    c->name);
   2003  1.23     lukem 		return;
   2004  1.23     lukem 	}
   2005  1.23     lukem 
   2006  1.23     lukem 	if (ep != NULL && *ep != '\0') {
   2007  1.23     lukem 		if (c->options != NULL)
   2008  1.23     lukem 			free(c->options);
   2009  1.23     lukem 		c->options = xstrdup(ep);
   2010  1.23     lukem 	}
   2011  1.23     lukem 	if (c->options != NULL)
   2012  1.23     lukem 		reply(200, "Options for %s are '%s'.", c->name, c->options);
   2013  1.23     lukem 	else
   2014  1.23     lukem 		reply(200, "No options defined for %s.", c->name);
   2015  1.23     lukem }
   2016