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