Home | History | Annotate | Line # | Download | only in ftpd
ftpcmd.y revision 1.21
      1  1.21     lukem /*	$NetBSD: ftpcmd.y,v 1.21 1998/09/05 17:33:00 lukem 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.21     lukem __RCSID("$NetBSD: ftpcmd.y,v 1.21 1998/09/05 17:33:00 lukem 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.4   deraadt 
     76   1.4   deraadt #include "extern.h"
     77   1.1       cgd 
     78   1.1       cgd extern	struct sockaddr_in data_dest;
     79   1.1       cgd extern	int logged_in;
     80   1.1       cgd extern	struct passwd *pw;
     81   1.1       cgd extern	int guest;
     82   1.1       cgd extern	int logging;
     83   1.1       cgd extern	int type;
     84   1.1       cgd extern	int form;
     85   1.1       cgd extern	int debug;
     86   1.1       cgd extern  int pdata;
     87   1.1       cgd extern	char hostname[], remotehost[];
     88   1.1       cgd extern	char proctitle[];
     89   1.1       cgd extern	int usedefault;
     90   1.1       cgd extern  int transflag;
     91   1.1       cgd extern  char tmpline[];
     92  1.12     lukem extern	struct ftpclass curclass;
     93  1.15       mrg extern	struct sockaddr_in his_addr;
     94   1.1       cgd 
     95   1.1       cgd off_t	restart_point;
     96   1.1       cgd 
     97   1.1       cgd static	int cmd_type;
     98   1.1       cgd static	int cmd_form;
     99   1.1       cgd static	int cmd_bytesz;
    100   1.1       cgd char	cbuf[512];
    101   1.1       cgd char	*fromname;
    102   1.1       cgd 
    103   1.1       cgd %}
    104   1.1       cgd 
    105   1.4   deraadt %union {
    106   1.4   deraadt 	int	i;
    107   1.4   deraadt 	char   *s;
    108   1.4   deraadt }
    109   1.4   deraadt 
    110   1.1       cgd %token
    111   1.1       cgd 	A	B	C	E	F	I
    112   1.1       cgd 	L	N	P	R	S	T
    113   1.1       cgd 
    114   1.4   deraadt 	SP	CRLF	COMMA
    115   1.1       cgd 
    116   1.1       cgd 	USER	PASS	ACCT	REIN	QUIT	PORT
    117   1.1       cgd 	PASV	TYPE	STRU	MODE	RETR	STOR
    118   1.1       cgd 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
    119   1.1       cgd 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
    120   1.1       cgd 	ABOR	DELE	CWD	LIST	NLST	SITE
    121   1.1       cgd 	STAT	HELP	NOOP	MKD	RMD	PWD
    122  1.21     lukem 	CDUP	STOU	SMNT	SYST
    123  1.21     lukem 
    124  1.21     lukem 	SIZE	MDTM
    125   1.1       cgd 
    126   1.1       cgd 	UMASK	IDLE	CHMOD
    127   1.1       cgd 
    128   1.1       cgd 	LEXERR
    129   1.1       cgd 
    130   1.4   deraadt %token	<s> STRING
    131   1.4   deraadt %token	<i> NUMBER
    132   1.4   deraadt 
    133  1.12     lukem %type	<i> check_login check_modify octal_number byte_size
    134   1.4   deraadt %type	<i> struct_code mode_code type_code form_code
    135   1.4   deraadt %type	<s> pathstring pathname password username
    136   1.4   deraadt 
    137   1.1       cgd %start	cmd_list
    138   1.1       cgd 
    139   1.1       cgd %%
    140   1.1       cgd 
    141   1.4   deraadt cmd_list
    142   1.4   deraadt 	: /* empty */
    143   1.4   deraadt 	| cmd_list cmd
    144   1.4   deraadt 		{
    145   1.1       cgd 			fromname = (char *) 0;
    146   1.1       cgd 			restart_point = (off_t) 0;
    147   1.1       cgd 		}
    148   1.4   deraadt 	| cmd_list rcmd
    149   1.1       cgd 	;
    150   1.1       cgd 
    151   1.4   deraadt cmd
    152   1.4   deraadt 	: USER SP username CRLF
    153   1.4   deraadt 		{
    154   1.4   deraadt 			user($3);
    155   1.4   deraadt 			free($3);
    156   1.4   deraadt 		}
    157   1.4   deraadt 	| PASS SP password CRLF
    158   1.4   deraadt 		{
    159   1.4   deraadt 			pass($3);
    160   1.4   deraadt 			free($3);
    161   1.1       cgd 		}
    162  1.15       mrg 	| PORT check_login SP host_port CRLF
    163   1.4   deraadt 		{
    164  1.15       mrg 			/* be paranoid, if told so */
    165  1.16       mrg 			if (curclass.checkportcmd &&
    166  1.15       mrg 			    ((ntohs(data_dest.sin_port) < IPPORT_RESERVED) ||
    167  1.15       mrg 			    memcmp(&data_dest.sin_addr, &his_addr.sin_addr,
    168  1.15       mrg 			    sizeof(data_dest.sin_addr)) != 0)) {
    169  1.15       mrg 				reply(500, "Illegal PORT command rejected");
    170  1.15       mrg 				return (NULL);
    171  1.15       mrg 			}
    172   1.1       cgd 			usedefault = 0;
    173   1.1       cgd 			if (pdata >= 0) {
    174   1.1       cgd 				(void) close(pdata);
    175   1.1       cgd 				pdata = -1;
    176   1.1       cgd 			}
    177   1.1       cgd 			reply(200, "PORT command successful.");
    178   1.1       cgd 		}
    179  1.20        tv 	| PASV check_login CRLF
    180   1.4   deraadt 		{
    181  1.20        tv 			if (curclass.passive) {
    182  1.20        tv 				passive();
    183  1.20        tv 			} else {
    184  1.20        tv 				reply(500, "PASV mode not available.");
    185  1.20        tv 			}
    186   1.1       cgd 		}
    187   1.4   deraadt 	| TYPE SP type_code CRLF
    188   1.4   deraadt 		{
    189   1.1       cgd 			switch (cmd_type) {
    190   1.1       cgd 
    191   1.1       cgd 			case TYPE_A:
    192   1.1       cgd 				if (cmd_form == FORM_N) {
    193   1.1       cgd 					reply(200, "Type set to A.");
    194   1.1       cgd 					type = cmd_type;
    195   1.1       cgd 					form = cmd_form;
    196   1.1       cgd 				} else
    197   1.1       cgd 					reply(504, "Form must be N.");
    198   1.1       cgd 				break;
    199   1.1       cgd 
    200   1.1       cgd 			case TYPE_E:
    201   1.1       cgd 				reply(504, "Type E not implemented.");
    202   1.1       cgd 				break;
    203   1.1       cgd 
    204   1.1       cgd 			case TYPE_I:
    205   1.1       cgd 				reply(200, "Type set to I.");
    206   1.1       cgd 				type = cmd_type;
    207   1.1       cgd 				break;
    208   1.1       cgd 
    209   1.1       cgd 			case TYPE_L:
    210   1.1       cgd #if NBBY == 8
    211   1.1       cgd 				if (cmd_bytesz == 8) {
    212   1.1       cgd 					reply(200,
    213   1.1       cgd 					    "Type set to L (byte size 8).");
    214   1.1       cgd 					type = cmd_type;
    215   1.1       cgd 				} else
    216   1.1       cgd 					reply(504, "Byte size must be 8.");
    217   1.1       cgd #else /* NBBY == 8 */
    218   1.1       cgd 				UNIMPLEMENTED for NBBY != 8
    219   1.1       cgd #endif /* NBBY == 8 */
    220   1.1       cgd 			}
    221   1.1       cgd 		}
    222   1.4   deraadt 	| STRU SP struct_code CRLF
    223   1.4   deraadt 		{
    224   1.1       cgd 			switch ($3) {
    225   1.1       cgd 
    226   1.1       cgd 			case STRU_F:
    227   1.1       cgd 				reply(200, "STRU F ok.");
    228   1.1       cgd 				break;
    229   1.1       cgd 
    230   1.1       cgd 			default:
    231   1.1       cgd 				reply(504, "Unimplemented STRU type.");
    232   1.1       cgd 			}
    233   1.1       cgd 		}
    234   1.4   deraadt 	| MODE SP mode_code CRLF
    235   1.4   deraadt 		{
    236   1.1       cgd 			switch ($3) {
    237   1.1       cgd 
    238   1.1       cgd 			case MODE_S:
    239   1.1       cgd 				reply(200, "MODE S ok.");
    240   1.1       cgd 				break;
    241   1.1       cgd 
    242   1.1       cgd 			default:
    243   1.1       cgd 				reply(502, "Unimplemented MODE type.");
    244   1.1       cgd 			}
    245   1.1       cgd 		}
    246   1.4   deraadt 	| ALLO SP NUMBER CRLF
    247   1.4   deraadt 		{
    248   1.1       cgd 			reply(202, "ALLO command ignored.");
    249   1.1       cgd 		}
    250   1.4   deraadt 	| ALLO SP NUMBER SP R SP NUMBER CRLF
    251   1.4   deraadt 		{
    252   1.1       cgd 			reply(202, "ALLO command ignored.");
    253   1.1       cgd 		}
    254   1.4   deraadt 	| RETR check_login SP pathname CRLF
    255   1.4   deraadt 		{
    256   1.1       cgd 			if ($2 && $4 != NULL)
    257   1.4   deraadt 				retrieve((char *) 0, $4);
    258   1.1       cgd 			if ($4 != NULL)
    259   1.4   deraadt 				free($4);
    260   1.1       cgd 		}
    261   1.4   deraadt 	| STOR check_login SP pathname CRLF
    262   1.4   deraadt 		{
    263   1.1       cgd 			if ($2 && $4 != NULL)
    264   1.4   deraadt 				store($4, "w", 0);
    265   1.1       cgd 			if ($4 != NULL)
    266   1.4   deraadt 				free($4);
    267   1.1       cgd 		}
    268   1.4   deraadt 	| APPE check_login SP pathname CRLF
    269   1.4   deraadt 		{
    270   1.1       cgd 			if ($2 && $4 != NULL)
    271   1.4   deraadt 				store($4, "a", 0);
    272   1.1       cgd 			if ($4 != NULL)
    273   1.4   deraadt 				free($4);
    274   1.1       cgd 		}
    275   1.4   deraadt 	| NLST check_login CRLF
    276   1.4   deraadt 		{
    277   1.1       cgd 			if ($2)
    278   1.1       cgd 				send_file_list(".");
    279   1.1       cgd 		}
    280   1.4   deraadt 	| NLST check_login SP STRING CRLF
    281   1.4   deraadt 		{
    282   1.4   deraadt 			if ($2 && $4 != NULL)
    283   1.4   deraadt 				send_file_list($4);
    284   1.1       cgd 			if ($4 != NULL)
    285   1.4   deraadt 				free($4);
    286   1.1       cgd 		}
    287   1.4   deraadt 	| LIST check_login CRLF
    288   1.4   deraadt 		{
    289   1.1       cgd 			if ($2)
    290   1.1       cgd 				retrieve("/bin/ls -lgA", "");
    291   1.1       cgd 		}
    292   1.4   deraadt 	| LIST check_login SP pathname CRLF
    293   1.4   deraadt 		{
    294   1.1       cgd 			if ($2 && $4 != NULL)
    295   1.4   deraadt 				retrieve("/bin/ls -lgA %s", $4);
    296   1.1       cgd 			if ($4 != NULL)
    297   1.4   deraadt 				free($4);
    298   1.1       cgd 		}
    299   1.4   deraadt 	| STAT check_login SP pathname CRLF
    300   1.4   deraadt 		{
    301   1.1       cgd 			if ($2 && $4 != NULL)
    302   1.4   deraadt 				statfilecmd($4);
    303   1.1       cgd 			if ($4 != NULL)
    304   1.4   deraadt 				free($4);
    305   1.1       cgd 		}
    306   1.4   deraadt 	| STAT CRLF
    307   1.4   deraadt 		{
    308   1.1       cgd 			statcmd();
    309   1.1       cgd 		}
    310  1.12     lukem 	| DELE check_modify SP pathname CRLF
    311   1.4   deraadt 		{
    312   1.1       cgd 			if ($2 && $4 != NULL)
    313   1.4   deraadt 				delete($4);
    314   1.1       cgd 			if ($4 != NULL)
    315   1.4   deraadt 				free($4);
    316   1.1       cgd 		}
    317   1.4   deraadt 	| RNTO SP pathname CRLF
    318   1.4   deraadt 		{
    319   1.1       cgd 			if (fromname) {
    320   1.4   deraadt 				renamecmd(fromname, $3);
    321   1.1       cgd 				free(fromname);
    322   1.1       cgd 				fromname = (char *) 0;
    323   1.1       cgd 			} else {
    324   1.1       cgd 				reply(503, "Bad sequence of commands.");
    325   1.1       cgd 			}
    326   1.4   deraadt 			free($3);
    327   1.1       cgd 		}
    328   1.4   deraadt 	| ABOR CRLF
    329   1.4   deraadt 		{
    330   1.1       cgd 			reply(225, "ABOR command successful.");
    331   1.1       cgd 		}
    332   1.4   deraadt 	| CWD check_login CRLF
    333   1.4   deraadt 		{
    334   1.1       cgd 			if ($2)
    335   1.1       cgd 				cwd(pw->pw_dir);
    336   1.1       cgd 		}
    337   1.4   deraadt 	| CWD check_login SP pathname CRLF
    338   1.4   deraadt 		{
    339   1.1       cgd 			if ($2 && $4 != NULL)
    340   1.4   deraadt 				cwd($4);
    341   1.1       cgd 			if ($4 != NULL)
    342   1.4   deraadt 				free($4);
    343   1.1       cgd 		}
    344   1.4   deraadt 	| HELP CRLF
    345   1.4   deraadt 		{
    346   1.1       cgd 			help(cmdtab, (char *) 0);
    347   1.1       cgd 		}
    348   1.4   deraadt 	| HELP SP STRING CRLF
    349   1.4   deraadt 		{
    350   1.4   deraadt 			char *cp = $3;
    351   1.1       cgd 
    352   1.1       cgd 			if (strncasecmp(cp, "SITE", 4) == 0) {
    353   1.4   deraadt 				cp = $3 + 4;
    354   1.1       cgd 				if (*cp == ' ')
    355   1.1       cgd 					cp++;
    356   1.1       cgd 				if (*cp)
    357   1.1       cgd 					help(sitetab, cp);
    358   1.1       cgd 				else
    359   1.1       cgd 					help(sitetab, (char *) 0);
    360   1.1       cgd 			} else
    361   1.4   deraadt 				help(cmdtab, $3);
    362   1.1       cgd 		}
    363   1.4   deraadt 	| NOOP CRLF
    364   1.4   deraadt 		{
    365   1.1       cgd 			reply(200, "NOOP command successful.");
    366   1.1       cgd 		}
    367  1.12     lukem 	| MKD check_modify SP pathname CRLF
    368   1.4   deraadt 		{
    369   1.1       cgd 			if ($2 && $4 != NULL)
    370   1.4   deraadt 				makedir($4);
    371   1.1       cgd 			if ($4 != NULL)
    372   1.4   deraadt 				free($4);
    373   1.1       cgd 		}
    374  1.12     lukem 	| RMD check_modify SP pathname CRLF
    375   1.4   deraadt 		{
    376   1.1       cgd 			if ($2 && $4 != NULL)
    377   1.4   deraadt 				removedir($4);
    378   1.1       cgd 			if ($4 != NULL)
    379   1.4   deraadt 				free($4);
    380   1.1       cgd 		}
    381   1.4   deraadt 	| PWD check_login CRLF
    382   1.4   deraadt 		{
    383   1.1       cgd 			if ($2)
    384   1.1       cgd 				pwd();
    385   1.1       cgd 		}
    386   1.4   deraadt 	| CDUP check_login CRLF
    387   1.4   deraadt 		{
    388   1.1       cgd 			if ($2)
    389   1.1       cgd 				cwd("..");
    390   1.1       cgd 		}
    391   1.4   deraadt 	| SITE SP HELP CRLF
    392   1.4   deraadt 		{
    393   1.1       cgd 			help(sitetab, (char *) 0);
    394   1.1       cgd 		}
    395   1.4   deraadt 	| SITE SP HELP SP STRING CRLF
    396   1.4   deraadt 		{
    397   1.4   deraadt 			help(sitetab, $5);
    398   1.1       cgd 		}
    399   1.4   deraadt 	| SITE SP UMASK check_login CRLF
    400   1.4   deraadt 		{
    401   1.1       cgd 			int oldmask;
    402   1.1       cgd 
    403   1.1       cgd 			if ($4) {
    404   1.1       cgd 				oldmask = umask(0);
    405   1.1       cgd 				(void) umask(oldmask);
    406   1.1       cgd 				reply(200, "Current UMASK is %03o", oldmask);
    407   1.1       cgd 			}
    408   1.1       cgd 		}
    409  1.12     lukem 	| SITE SP UMASK check_modify SP octal_number CRLF
    410   1.4   deraadt 		{
    411   1.1       cgd 			int oldmask;
    412   1.1       cgd 
    413   1.1       cgd 			if ($4) {
    414   1.1       cgd 				if (($6 == -1) || ($6 > 0777)) {
    415   1.1       cgd 					reply(501, "Bad UMASK value");
    416   1.1       cgd 				} else {
    417   1.1       cgd 					oldmask = umask($6);
    418   1.1       cgd 					reply(200,
    419   1.1       cgd 					    "UMASK set to %03o (was %03o)",
    420   1.1       cgd 					    $6, oldmask);
    421   1.1       cgd 				}
    422   1.1       cgd 			}
    423   1.1       cgd 		}
    424  1.12     lukem 	| SITE SP CHMOD check_modify SP octal_number SP pathname CRLF
    425   1.4   deraadt 		{
    426   1.1       cgd 			if ($4 && ($8 != NULL)) {
    427   1.1       cgd 				if ($6 > 0777)
    428   1.1       cgd 					reply(501,
    429   1.1       cgd 				"CHMOD: Mode value must be between 0 and 0777");
    430   1.4   deraadt 				else if (chmod($8, $6) < 0)
    431   1.4   deraadt 					perror_reply(550, $8);
    432   1.1       cgd 				else
    433   1.1       cgd 					reply(200, "CHMOD command successful.");
    434   1.1       cgd 			}
    435   1.1       cgd 			if ($8 != NULL)
    436   1.4   deraadt 				free($8);
    437   1.1       cgd 		}
    438   1.4   deraadt 	| SITE SP IDLE CRLF
    439   1.4   deraadt 		{
    440   1.1       cgd 			reply(200,
    441   1.1       cgd 			    "Current IDLE time limit is %d seconds; max %d",
    442  1.12     lukem 				curclass.timeout, curclass.maxtimeout);
    443   1.1       cgd 		}
    444   1.4   deraadt 	| SITE SP IDLE SP NUMBER CRLF
    445   1.4   deraadt 		{
    446  1.12     lukem 			if ($5 < 30 || $5 > curclass.maxtimeout) {
    447   1.1       cgd 				reply(501,
    448  1.12     lukem 			"IDLE time limit must be between 30 and %d seconds",
    449  1.12     lukem 				    curclass.maxtimeout);
    450   1.1       cgd 			} else {
    451  1.12     lukem 				curclass.timeout = $5;
    452  1.12     lukem 				(void) alarm(curclass.timeout);
    453   1.1       cgd 				reply(200,
    454  1.12     lukem 				    "IDLE time limit set to %d seconds",
    455  1.12     lukem 				    curclass.timeout);
    456   1.1       cgd 			}
    457   1.1       cgd 		}
    458   1.4   deraadt 	| STOU check_login SP pathname CRLF
    459   1.4   deraadt 		{
    460   1.1       cgd 			if ($2 && $4 != NULL)
    461   1.4   deraadt 				store($4, "w", 1);
    462   1.1       cgd 			if ($4 != NULL)
    463   1.4   deraadt 				free($4);
    464   1.1       cgd 		}
    465   1.4   deraadt 	| SYST CRLF
    466   1.4   deraadt 		{
    467   1.1       cgd #ifdef unix
    468   1.1       cgd #ifdef BSD
    469   1.1       cgd 			reply(215, "UNIX Type: L%d Version: BSD-%d",
    470   1.1       cgd 				NBBY, BSD);
    471   1.1       cgd #else /* BSD */
    472   1.1       cgd 			reply(215, "UNIX Type: L%d", NBBY);
    473   1.1       cgd #endif /* BSD */
    474   1.1       cgd #else /* unix */
    475   1.1       cgd 			reply(215, "UNKNOWN Type: L%d", NBBY);
    476   1.1       cgd #endif /* unix */
    477   1.1       cgd 		}
    478   1.1       cgd 
    479   1.1       cgd 		/*
    480  1.21     lukem 		 * SIZE is not in RFC 959, but Postel has blessed it and
    481   1.1       cgd 		 * it will be in the updated RFC.
    482   1.1       cgd 		 *
    483   1.1       cgd 		 * Return size of file in a format suitable for
    484   1.1       cgd 		 * using with RESTART (we just count bytes).
    485   1.1       cgd 		 */
    486   1.4   deraadt 	| SIZE check_login SP pathname CRLF
    487   1.4   deraadt 		{
    488   1.1       cgd 			if ($2 && $4 != NULL)
    489   1.4   deraadt 				sizecmd($4);
    490   1.1       cgd 			if ($4 != NULL)
    491   1.4   deraadt 				free($4);
    492   1.1       cgd 		}
    493   1.1       cgd 
    494   1.1       cgd 		/*
    495  1.21     lukem 		 * MDTM is not in RFC 959, but Postel has blessed it and
    496   1.1       cgd 		 * it will be in the updated RFC.
    497   1.1       cgd 		 *
    498   1.1       cgd 		 * Return modification time of file as an ISO 3307
    499   1.1       cgd 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
    500   1.1       cgd 		 * where xxx is the fractional second (of any precision,
    501   1.1       cgd 		 * not necessarily 3 digits)
    502   1.1       cgd 		 */
    503   1.4   deraadt 	| MDTM check_login SP pathname CRLF
    504   1.4   deraadt 		{
    505   1.1       cgd 			if ($2 && $4 != NULL) {
    506   1.1       cgd 				struct stat stbuf;
    507   1.4   deraadt 				if (stat($4, &stbuf) < 0)
    508   1.4   deraadt 					reply(550, "%s: %s",
    509   1.4   deraadt 					    $4, strerror(errno));
    510   1.4   deraadt 				else if (!S_ISREG(stbuf.st_mode)) {
    511   1.4   deraadt 					reply(550, "%s: not a plain file.", $4);
    512   1.1       cgd 				} else {
    513   1.4   deraadt 					struct tm *t;
    514   1.1       cgd 					t = gmtime(&stbuf.st_mtime);
    515   1.1       cgd 					reply(213,
    516   1.7       jtc 					    "%04d%02d%02d%02d%02d%02d",
    517  1.18     lukem 					    TM_YEAR_BASE + t->tm_year,
    518   1.7       jtc 					    t->tm_mon+1, t->tm_mday,
    519   1.1       cgd 					    t->tm_hour, t->tm_min, t->tm_sec);
    520   1.1       cgd 				}
    521   1.1       cgd 			}
    522   1.1       cgd 			if ($4 != NULL)
    523   1.4   deraadt 				free($4);
    524   1.1       cgd 		}
    525   1.4   deraadt 	| QUIT CRLF
    526   1.4   deraadt 		{
    527   1.1       cgd 			reply(221, "Goodbye.");
    528   1.1       cgd 			dologout(0);
    529   1.1       cgd 		}
    530   1.4   deraadt 	| error CRLF
    531   1.4   deraadt 		{
    532   1.1       cgd 			yyerrok;
    533   1.1       cgd 		}
    534   1.1       cgd 	;
    535  1.21     lukem 
    536   1.4   deraadt rcmd
    537  1.17       cjs 	: RNFR check_modify SP pathname CRLF
    538   1.4   deraadt 		{
    539   1.1       cgd 			restart_point = (off_t) 0;
    540   1.1       cgd 			if ($2 && $4) {
    541   1.4   deraadt 				fromname = renamefrom($4);
    542   1.1       cgd 				if (fromname == (char *) 0 && $4) {
    543   1.4   deraadt 					free($4);
    544   1.1       cgd 				}
    545   1.1       cgd 			}
    546   1.1       cgd 		}
    547   1.4   deraadt 	| REST SP byte_size CRLF
    548   1.4   deraadt 		{
    549   1.1       cgd 			fromname = (char *) 0;
    550   1.4   deraadt 			restart_point = $3;	/* XXX $3 is only "int" */
    551   1.4   deraadt 			reply(350, "Restarting at %qd. %s", restart_point,
    552   1.1       cgd 			    "Send STORE or RETRIEVE to initiate transfer.");
    553   1.1       cgd 		}
    554   1.1       cgd 	;
    555   1.4   deraadt 
    556   1.4   deraadt username
    557   1.4   deraadt 	: STRING
    558   1.1       cgd 	;
    559   1.1       cgd 
    560   1.4   deraadt password
    561   1.4   deraadt 	: /* empty */
    562   1.4   deraadt 		{
    563   1.4   deraadt 			$$ = (char *)calloc(1, sizeof(char));
    564   1.1       cgd 		}
    565   1.4   deraadt 	| STRING
    566   1.1       cgd 	;
    567   1.1       cgd 
    568   1.4   deraadt byte_size
    569   1.4   deraadt 	: NUMBER
    570   1.1       cgd 	;
    571   1.1       cgd 
    572   1.4   deraadt host_port
    573   1.4   deraadt 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    574   1.1       cgd 		NUMBER COMMA NUMBER
    575   1.4   deraadt 		{
    576   1.4   deraadt 			char *a, *p;
    577   1.1       cgd 
    578   1.6   mycroft 			data_dest.sin_len = sizeof(struct sockaddr_in);
    579   1.6   mycroft 			data_dest.sin_family = AF_INET;
    580   1.6   mycroft 			p = (char *)&data_dest.sin_port;
    581   1.6   mycroft 			p[0] = $9; p[1] = $11;
    582   1.1       cgd 			a = (char *)&data_dest.sin_addr;
    583   1.1       cgd 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
    584   1.1       cgd 		}
    585   1.1       cgd 	;
    586   1.1       cgd 
    587   1.4   deraadt form_code
    588   1.4   deraadt 	: N
    589   1.4   deraadt 		{
    590   1.4   deraadt 			$$ = FORM_N;
    591   1.4   deraadt 		}
    592   1.4   deraadt 	| T
    593   1.4   deraadt 		{
    594   1.4   deraadt 			$$ = FORM_T;
    595   1.4   deraadt 		}
    596   1.4   deraadt 	| C
    597   1.4   deraadt 		{
    598   1.4   deraadt 			$$ = FORM_C;
    599   1.4   deraadt 		}
    600   1.1       cgd 	;
    601   1.1       cgd 
    602   1.4   deraadt type_code
    603   1.4   deraadt 	: A
    604   1.4   deraadt 		{
    605   1.4   deraadt 			cmd_type = TYPE_A;
    606   1.4   deraadt 			cmd_form = FORM_N;
    607   1.4   deraadt 		}
    608   1.4   deraadt 	| A SP form_code
    609   1.4   deraadt 		{
    610   1.4   deraadt 			cmd_type = TYPE_A;
    611   1.4   deraadt 			cmd_form = $3;
    612   1.4   deraadt 		}
    613   1.4   deraadt 	| E
    614   1.4   deraadt 		{
    615   1.4   deraadt 			cmd_type = TYPE_E;
    616   1.4   deraadt 			cmd_form = FORM_N;
    617   1.4   deraadt 		}
    618   1.4   deraadt 	| E SP form_code
    619   1.4   deraadt 		{
    620   1.4   deraadt 			cmd_type = TYPE_E;
    621   1.4   deraadt 			cmd_form = $3;
    622   1.4   deraadt 		}
    623   1.4   deraadt 	| I
    624   1.4   deraadt 		{
    625   1.4   deraadt 			cmd_type = TYPE_I;
    626   1.4   deraadt 		}
    627   1.4   deraadt 	| L
    628   1.4   deraadt 		{
    629   1.4   deraadt 			cmd_type = TYPE_L;
    630   1.4   deraadt 			cmd_bytesz = NBBY;
    631   1.4   deraadt 		}
    632   1.4   deraadt 	| L SP byte_size
    633   1.4   deraadt 		{
    634   1.4   deraadt 			cmd_type = TYPE_L;
    635   1.4   deraadt 			cmd_bytesz = $3;
    636   1.4   deraadt 		}
    637   1.4   deraadt 		/* this is for a bug in the BBN ftp */
    638   1.4   deraadt 	| L byte_size
    639   1.4   deraadt 		{
    640   1.4   deraadt 			cmd_type = TYPE_L;
    641   1.4   deraadt 			cmd_bytesz = $2;
    642   1.4   deraadt 		}
    643   1.1       cgd 	;
    644   1.1       cgd 
    645   1.4   deraadt struct_code
    646   1.4   deraadt 	: F
    647   1.4   deraadt 		{
    648   1.4   deraadt 			$$ = STRU_F;
    649   1.4   deraadt 		}
    650   1.4   deraadt 	| R
    651   1.4   deraadt 		{
    652   1.4   deraadt 			$$ = STRU_R;
    653   1.4   deraadt 		}
    654   1.4   deraadt 	| P
    655   1.4   deraadt 		{
    656   1.4   deraadt 			$$ = STRU_P;
    657   1.4   deraadt 		}
    658   1.1       cgd 	;
    659   1.1       cgd 
    660   1.4   deraadt mode_code
    661   1.4   deraadt 	: S
    662   1.4   deraadt 		{
    663   1.4   deraadt 			$$ = MODE_S;
    664   1.4   deraadt 		}
    665   1.4   deraadt 	| B
    666   1.4   deraadt 		{
    667   1.4   deraadt 			$$ = MODE_B;
    668   1.4   deraadt 		}
    669   1.4   deraadt 	| C
    670   1.4   deraadt 		{
    671   1.4   deraadt 			$$ = MODE_C;
    672   1.4   deraadt 		}
    673   1.1       cgd 	;
    674   1.1       cgd 
    675   1.4   deraadt pathname
    676   1.4   deraadt 	: pathstring
    677   1.4   deraadt 		{
    678   1.4   deraadt 			/*
    679   1.4   deraadt 			 * Problem: this production is used for all pathname
    680   1.4   deraadt 			 * processing, but only gives a 550 error reply.
    681   1.9     lukem 			 * This is a valid reply in some cases but not in
    682   1.9     lukem 			 * others.
    683   1.4   deraadt 			 */
    684   1.4   deraadt 			if (logged_in && $1 && *$1 == '~') {
    685   1.4   deraadt 				glob_t gl;
    686   1.4   deraadt 				int flags =
    687  1.19    kleink 				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
    688   1.4   deraadt 
    689   1.9     lukem 				if ($1[1] == '\0')
    690   1.9     lukem 					$$ = strdup(pw->pw_dir);
    691   1.9     lukem 				else {
    692   1.9     lukem 					memset(&gl, 0, sizeof(gl));
    693   1.9     lukem 					if (glob($1, flags, NULL, &gl) ||
    694   1.9     lukem 					    gl.gl_pathc == 0) {
    695   1.9     lukem 						reply(550, "not found");
    696   1.9     lukem 						$$ = NULL;
    697   1.9     lukem 					} else
    698   1.9     lukem 						$$ = strdup(gl.gl_pathv[0]);
    699   1.9     lukem 					globfree(&gl);
    700   1.4   deraadt 				}
    701   1.4   deraadt 				free($1);
    702   1.4   deraadt 			} else
    703   1.4   deraadt 				$$ = $1;
    704   1.4   deraadt 		}
    705   1.1       cgd 	;
    706   1.1       cgd 
    707   1.4   deraadt pathstring
    708   1.4   deraadt 	: STRING
    709   1.1       cgd 	;
    710   1.1       cgd 
    711   1.4   deraadt octal_number
    712   1.4   deraadt 	: NUMBER
    713   1.4   deraadt 		{
    714   1.4   deraadt 			int ret, dec, multby, digit;
    715   1.1       cgd 
    716   1.4   deraadt 			/*
    717   1.4   deraadt 			 * Convert a number that was read as decimal number
    718   1.4   deraadt 			 * to what it would be if it had been read as octal.
    719   1.4   deraadt 			 */
    720   1.4   deraadt 			dec = $1;
    721   1.4   deraadt 			multby = 1;
    722   1.4   deraadt 			ret = 0;
    723   1.4   deraadt 			while (dec) {
    724   1.4   deraadt 				digit = dec%10;
    725   1.4   deraadt 				if (digit > 7) {
    726   1.4   deraadt 					ret = -1;
    727   1.4   deraadt 					break;
    728   1.4   deraadt 				}
    729   1.4   deraadt 				ret += digit * multby;
    730   1.4   deraadt 				multby *= 8;
    731   1.4   deraadt 				dec /= 10;
    732   1.1       cgd 			}
    733   1.4   deraadt 			$$ = ret;
    734   1.1       cgd 		}
    735   1.1       cgd 	;
    736   1.1       cgd 
    737   1.4   deraadt check_login
    738   1.4   deraadt 	: /* empty */
    739   1.4   deraadt 		{
    740   1.4   deraadt 			if (logged_in)
    741   1.4   deraadt 				$$ = 1;
    742   1.4   deraadt 			else {
    743   1.4   deraadt 				reply(530, "Please login with USER and PASS.");
    744   1.4   deraadt 				$$ = 0;
    745   1.4   deraadt 			}
    746   1.1       cgd 		}
    747   1.1       cgd 	;
    748  1.21     lukem 
    749  1.12     lukem check_modify
    750   1.8       cjs 	: /* empty */
    751   1.8       cjs 		{
    752   1.8       cjs 			if (logged_in)  {
    753  1.12     lukem 				if (curclass.modify) {
    754  1.12     lukem 					$$ = 1;
    755  1.14   hannken 				} else {
    756   1.8       cjs 					reply(502,
    757  1.12     lukem 					"No permission to use this command.");
    758   1.8       cjs 					$$ = 0;
    759  1.14   hannken 				}
    760   1.8       cjs 			} else {
    761   1.8       cjs 				reply(530, "Please login with USER and PASS.");
    762   1.8       cjs 				$$ = 0;
    763   1.8       cjs 			}
    764   1.8       cjs 		}
    765   1.1       cgd 
    766   1.1       cgd %%
    767   1.1       cgd 
    768   1.1       cgd extern jmp_buf errcatch;
    769   1.1       cgd 
    770   1.1       cgd #define	CMD	0	/* beginning of command */
    771   1.1       cgd #define	ARGS	1	/* expect miscellaneous arguments */
    772   1.1       cgd #define	STR1	2	/* expect SP followed by STRING */
    773   1.1       cgd #define	STR2	3	/* expect STRING */
    774   1.1       cgd #define	OSTR	4	/* optional SP then STRING */
    775   1.1       cgd #define	ZSTR1	5	/* SP then optional STRING */
    776   1.1       cgd #define	ZSTR2	6	/* optional STRING after SP */
    777   1.1       cgd #define	SITECMD	7	/* SITE command */
    778   1.1       cgd #define	NSTR	8	/* Number followed by a string */
    779  1.21     lukem #define NOARGS	9	/* No arguments allowed */
    780   1.1       cgd 
    781   1.1       cgd struct tab {
    782   1.1       cgd 	char	*name;
    783   1.1       cgd 	short	token;
    784   1.1       cgd 	short	state;
    785   1.1       cgd 	short	implemented;	/* 1 if command is implemented */
    786   1.1       cgd 	char	*help;
    787   1.1       cgd };
    788   1.1       cgd 
    789  1.21     lukem struct tab cmdtab[] = {
    790  1.21     lukem 				/* From RFC 959, in order defined (5.3.1) */
    791   1.1       cgd 	{ "USER", USER, STR1, 1,	"<sp> username" },
    792   1.1       cgd 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
    793   1.1       cgd 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
    794  1.21     lukem 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
    795  1.21     lukem 	{ "CDUP", CDUP, NOARGS, 1,	"(change to parent directory)" },
    796   1.1       cgd 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
    797  1.21     lukem 	{ "QUIT", QUIT, NOARGS, 1,	"(terminate service)", },
    798  1.21     lukem 	{ "REIN", REIN, NOARGS, 0,	"(reinitialize server state)" },
    799   1.1       cgd 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
    800  1.21     lukem 	{ "PASV", PASV, NOARGS, 1,	"(set server in passive mode)" },
    801   1.1       cgd 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
    802   1.1       cgd 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
    803   1.1       cgd 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
    804   1.1       cgd 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
    805   1.1       cgd 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
    806  1.21     lukem 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
    807   1.1       cgd 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
    808   1.1       cgd 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
    809   1.4   deraadt 	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
    810   1.1       cgd 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
    811   1.1       cgd 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
    812  1.21     lukem 	{ "ABOR", ABOR, NOARGS, 1,	"(abort operation)" },
    813   1.1       cgd 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
    814  1.21     lukem 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
    815  1.21     lukem 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
    816  1.21     lukem 	{ "PWD",  PWD,  NOARGS, 1,	"(return current directory)" },
    817   1.1       cgd 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
    818   1.1       cgd 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
    819   1.1       cgd 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
    820  1.21     lukem 	{ "SYST", SYST, NOARGS, 1,	"(get type of operating system)" },
    821   1.1       cgd 	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
    822   1.1       cgd 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
    823  1.21     lukem 	{ "NOOP", NOOP, NOARGS, 1,	"" },
    824  1.21     lukem 
    825   1.1       cgd 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
    826   1.1       cgd 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
    827  1.21     lukem 
    828  1.21     lukem 				/* obsolete commands */
    829  1.21     lukem 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
    830  1.21     lukem 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
    831  1.21     lukem 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
    832  1.21     lukem 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
    833  1.21     lukem 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
    834  1.21     lukem 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
    835  1.21     lukem 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
    836  1.21     lukem 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
    837  1.21     lukem 	{ "XCUP", CDUP, NOARGS, 1,	"(change to parent directory)" },
    838  1.21     lukem 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
    839  1.21     lukem 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
    840  1.21     lukem 	{ "XPWD", PWD,  NOARGS, 1,	"(return current directory)" },
    841  1.21     lukem 
    842   1.1       cgd 	{ NULL,   0,    0,    0,	0 }
    843   1.1       cgd };
    844   1.1       cgd 
    845   1.1       cgd struct tab sitetab[] = {
    846   1.1       cgd 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
    847   1.1       cgd 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
    848   1.1       cgd 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
    849   1.1       cgd 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
    850   1.1       cgd 	{ NULL,   0,    0,    0,	0 }
    851   1.1       cgd };
    852   1.1       cgd 
    853   1.4   deraadt static char	*copy __P((char *));
    854   1.4   deraadt static void	 help __P((struct tab *, char *));
    855   1.4   deraadt static struct tab *
    856   1.4   deraadt 		 lookup __P((struct tab *, char *));
    857   1.4   deraadt static void	 sizecmd __P((char *));
    858   1.4   deraadt static void	 toolong __P((int));
    859   1.4   deraadt static int	 yylex __P((void));
    860   1.4   deraadt 
    861   1.4   deraadt static struct tab *
    862   1.1       cgd lookup(p, cmd)
    863   1.4   deraadt 	struct tab *p;
    864   1.1       cgd 	char *cmd;
    865   1.1       cgd {
    866   1.1       cgd 
    867   1.1       cgd 	for (; p->name != NULL; p++)
    868   1.1       cgd 		if (strcmp(cmd, p->name) == 0)
    869   1.1       cgd 			return (p);
    870   1.1       cgd 	return (0);
    871   1.1       cgd }
    872   1.1       cgd 
    873   1.1       cgd #include <arpa/telnet.h>
    874   1.1       cgd 
    875   1.1       cgd /*
    876   1.1       cgd  * getline - a hacked up version of fgets to ignore TELNET escape codes.
    877   1.1       cgd  */
    878   1.1       cgd char *
    879   1.1       cgd getline(s, n, iop)
    880   1.1       cgd 	char *s;
    881   1.4   deraadt 	int n;
    882   1.4   deraadt 	FILE *iop;
    883   1.1       cgd {
    884   1.4   deraadt 	int c;
    885  1.21     lukem 	char *cs;
    886   1.1       cgd 
    887   1.1       cgd 	cs = s;
    888   1.1       cgd /* tmpline may contain saved command from urgent mode interruption */
    889   1.1       cgd 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
    890   1.1       cgd 		*cs++ = tmpline[c];
    891   1.1       cgd 		if (tmpline[c] == '\n') {
    892   1.1       cgd 			*cs++ = '\0';
    893   1.1       cgd 			if (debug)
    894   1.1       cgd 				syslog(LOG_DEBUG, "command: %s", s);
    895   1.1       cgd 			tmpline[0] = '\0';
    896   1.1       cgd 			return(s);
    897   1.1       cgd 		}
    898   1.1       cgd 		if (c == 0)
    899   1.1       cgd 			tmpline[0] = '\0';
    900   1.1       cgd 	}
    901   1.1       cgd 	while ((c = getc(iop)) != EOF) {
    902   1.1       cgd 		c &= 0377;
    903   1.1       cgd 		if (c == IAC) {
    904   1.1       cgd 		    if ((c = getc(iop)) != EOF) {
    905   1.1       cgd 			c &= 0377;
    906   1.1       cgd 			switch (c) {
    907   1.1       cgd 			case WILL:
    908   1.1       cgd 			case WONT:
    909   1.1       cgd 				c = getc(iop);
    910   1.1       cgd 				printf("%c%c%c", IAC, DONT, 0377&c);
    911   1.1       cgd 				(void) fflush(stdout);
    912   1.1       cgd 				continue;
    913   1.1       cgd 			case DO:
    914   1.1       cgd 			case DONT:
    915   1.1       cgd 				c = getc(iop);
    916   1.1       cgd 				printf("%c%c%c", IAC, WONT, 0377&c);
    917   1.1       cgd 				(void) fflush(stdout);
    918   1.1       cgd 				continue;
    919   1.1       cgd 			case IAC:
    920   1.1       cgd 				break;
    921   1.1       cgd 			default:
    922   1.1       cgd 				continue;	/* ignore command */
    923   1.1       cgd 			}
    924   1.1       cgd 		    }
    925   1.1       cgd 		}
    926   1.1       cgd 		*cs++ = c;
    927   1.1       cgd 		if (--n <= 0 || c == '\n')
    928   1.1       cgd 			break;
    929   1.1       cgd 	}
    930   1.1       cgd 	if (c == EOF && cs == s)
    931   1.1       cgd 		return (NULL);
    932   1.1       cgd 	*cs++ = '\0';
    933   1.4   deraadt 	if (debug) {
    934   1.4   deraadt 		if (!guest && strncasecmp("pass ", s, 5) == 0) {
    935   1.4   deraadt 			/* Don't syslog passwords */
    936   1.4   deraadt 			syslog(LOG_DEBUG, "command: %.5s ???", s);
    937   1.4   deraadt 		} else {
    938  1.21     lukem 			char *cp;
    939  1.21     lukem 			int len;
    940   1.4   deraadt 
    941   1.4   deraadt 			/* Don't syslog trailing CR-LF */
    942   1.4   deraadt 			len = strlen(s);
    943   1.4   deraadt 			cp = s + len - 1;
    944   1.4   deraadt 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
    945   1.4   deraadt 				--cp;
    946   1.4   deraadt 				--len;
    947   1.4   deraadt 			}
    948   1.4   deraadt 			syslog(LOG_DEBUG, "command: %.*s", len, s);
    949   1.4   deraadt 		}
    950   1.4   deraadt 	}
    951   1.1       cgd 	return (s);
    952   1.1       cgd }
    953   1.1       cgd 
    954   1.1       cgd static void
    955   1.4   deraadt toolong(signo)
    956   1.4   deraadt 	int signo;
    957   1.1       cgd {
    958   1.1       cgd 
    959   1.1       cgd 	reply(421,
    960  1.12     lukem 	    "Timeout (%d seconds): closing control connection.",
    961  1.12     lukem 	    curclass.timeout);
    962   1.4   deraadt 	if (logging)
    963   1.4   deraadt 		syslog(LOG_INFO, "User %s timed out after %d seconds",
    964  1.12     lukem 		    (pw ? pw -> pw_name : "unknown"), curclass.timeout);
    965   1.1       cgd 	dologout(1);
    966   1.1       cgd }
    967   1.1       cgd 
    968   1.4   deraadt static int
    969   1.1       cgd yylex()
    970   1.1       cgd {
    971   1.1       cgd 	static int cpos, state;
    972   1.4   deraadt 	char *cp, *cp2;
    973   1.4   deraadt 	struct tab *p;
    974  1.21     lukem 	int n, errored;
    975   1.4   deraadt 	char c;
    976   1.1       cgd 
    977   1.1       cgd 	for (;;) {
    978  1.21     lukem 		errored = 0;
    979   1.1       cgd 		switch (state) {
    980   1.1       cgd 
    981   1.1       cgd 		case CMD:
    982   1.1       cgd 			(void) signal(SIGALRM, toolong);
    983  1.12     lukem 			(void) alarm(curclass.timeout);
    984   1.1       cgd 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
    985   1.1       cgd 				reply(221, "You could at least say goodbye.");
    986   1.1       cgd 				dologout(0);
    987   1.1       cgd 			}
    988   1.1       cgd 			(void) alarm(0);
    989   1.3       cgd #ifdef HASSETPROCTITLE
    990  1.10        pk 			if (strncasecmp(cbuf, "PASS", 4) != 0)
    991   1.1       cgd 				setproctitle("%s: %s", proctitle, cbuf);
    992   1.3       cgd #endif /* HASSETPROCTITLE */
    993   1.4   deraadt 			if ((cp = strchr(cbuf, '\r'))) {
    994   1.1       cgd 				*cp++ = '\n';
    995   1.1       cgd 				*cp = '\0';
    996   1.1       cgd 			}
    997   1.1       cgd 			if ((cp = strpbrk(cbuf, " \n")))
    998   1.1       cgd 				cpos = cp - cbuf;
    999   1.1       cgd 			if (cpos == 0)
   1000   1.1       cgd 				cpos = 4;
   1001   1.1       cgd 			c = cbuf[cpos];
   1002   1.1       cgd 			cbuf[cpos] = '\0';
   1003   1.1       cgd 			upper(cbuf);
   1004   1.1       cgd 			p = lookup(cmdtab, cbuf);
   1005   1.1       cgd 			cbuf[cpos] = c;
   1006   1.1       cgd 			if (p != 0) {
   1007   1.1       cgd 				if (p->implemented == 0) {
   1008   1.1       cgd 					nack(p->name);
   1009  1.21     lukem 					errored = 1;
   1010  1.21     lukem 					break;
   1011   1.1       cgd 				}
   1012   1.1       cgd 				state = p->state;
   1013   1.4   deraadt 				yylval.s = p->name;
   1014   1.1       cgd 				return (p->token);
   1015   1.1       cgd 			}
   1016   1.1       cgd 			break;
   1017   1.1       cgd 
   1018   1.1       cgd 		case SITECMD:
   1019   1.1       cgd 			if (cbuf[cpos] == ' ') {
   1020   1.1       cgd 				cpos++;
   1021   1.1       cgd 				return (SP);
   1022   1.1       cgd 			}
   1023   1.1       cgd 			cp = &cbuf[cpos];
   1024   1.1       cgd 			if ((cp2 = strpbrk(cp, " \n")))
   1025   1.1       cgd 				cpos = cp2 - cbuf;
   1026   1.1       cgd 			c = cbuf[cpos];
   1027   1.1       cgd 			cbuf[cpos] = '\0';
   1028   1.1       cgd 			upper(cp);
   1029   1.1       cgd 			p = lookup(sitetab, cp);
   1030   1.1       cgd 			cbuf[cpos] = c;
   1031   1.1       cgd 			if (p != 0) {
   1032   1.1       cgd 				if (p->implemented == 0) {
   1033   1.1       cgd 					nack(p->name);
   1034  1.21     lukem 					errored = 1;
   1035  1.21     lukem 					break;
   1036   1.1       cgd 				}
   1037   1.1       cgd 				state = p->state;
   1038   1.4   deraadt 				yylval.s = p->name;
   1039   1.1       cgd 				return (p->token);
   1040   1.1       cgd 			}
   1041   1.1       cgd 			state = CMD;
   1042   1.1       cgd 			break;
   1043   1.1       cgd 
   1044   1.1       cgd 		case OSTR:
   1045   1.1       cgd 			if (cbuf[cpos] == '\n') {
   1046   1.1       cgd 				state = CMD;
   1047   1.1       cgd 				return (CRLF);
   1048   1.1       cgd 			}
   1049   1.1       cgd 			/* FALLTHROUGH */
   1050   1.1       cgd 
   1051   1.1       cgd 		case STR1:
   1052   1.1       cgd 		case ZSTR1:
   1053   1.1       cgd 		dostr1:
   1054   1.1       cgd 			if (cbuf[cpos] == ' ') {
   1055   1.1       cgd 				cpos++;
   1056   1.1       cgd 				state = state == OSTR ? STR2 : ++state;
   1057   1.1       cgd 				return (SP);
   1058   1.1       cgd 			}
   1059   1.1       cgd 			break;
   1060   1.1       cgd 
   1061   1.1       cgd 		case ZSTR2:
   1062   1.1       cgd 			if (cbuf[cpos] == '\n') {
   1063   1.1       cgd 				state = CMD;
   1064   1.1       cgd 				return (CRLF);
   1065   1.1       cgd 			}
   1066   1.1       cgd 			/* FALLTHROUGH */
   1067   1.1       cgd 
   1068   1.1       cgd 		case STR2:
   1069   1.1       cgd 			cp = &cbuf[cpos];
   1070   1.1       cgd 			n = strlen(cp);
   1071   1.1       cgd 			cpos += n - 1;
   1072   1.1       cgd 			/*
   1073   1.1       cgd 			 * Make sure the string is nonempty and \n terminated.
   1074   1.1       cgd 			 */
   1075   1.1       cgd 			if (n > 1 && cbuf[cpos] == '\n') {
   1076   1.1       cgd 				cbuf[cpos] = '\0';
   1077   1.4   deraadt 				yylval.s = copy(cp);
   1078   1.1       cgd 				cbuf[cpos] = '\n';
   1079   1.1       cgd 				state = ARGS;
   1080   1.1       cgd 				return (STRING);
   1081   1.1       cgd 			}
   1082   1.1       cgd 			break;
   1083   1.1       cgd 
   1084   1.1       cgd 		case NSTR:
   1085   1.1       cgd 			if (cbuf[cpos] == ' ') {
   1086   1.1       cgd 				cpos++;
   1087   1.1       cgd 				return (SP);
   1088   1.1       cgd 			}
   1089   1.1       cgd 			if (isdigit(cbuf[cpos])) {
   1090   1.1       cgd 				cp = &cbuf[cpos];
   1091   1.1       cgd 				while (isdigit(cbuf[++cpos]))
   1092   1.1       cgd 					;
   1093   1.1       cgd 				c = cbuf[cpos];
   1094   1.1       cgd 				cbuf[cpos] = '\0';
   1095   1.4   deraadt 				yylval.i = atoi(cp);
   1096   1.1       cgd 				cbuf[cpos] = c;
   1097   1.1       cgd 				state = STR1;
   1098   1.1       cgd 				return (NUMBER);
   1099   1.1       cgd 			}
   1100   1.1       cgd 			state = STR1;
   1101   1.1       cgd 			goto dostr1;
   1102   1.1       cgd 
   1103   1.1       cgd 		case ARGS:
   1104   1.1       cgd 			if (isdigit(cbuf[cpos])) {
   1105   1.1       cgd 				cp = &cbuf[cpos];
   1106   1.1       cgd 				while (isdigit(cbuf[++cpos]))
   1107   1.1       cgd 					;
   1108   1.1       cgd 				c = cbuf[cpos];
   1109   1.1       cgd 				cbuf[cpos] = '\0';
   1110   1.4   deraadt 				yylval.i = atoi(cp);
   1111   1.1       cgd 				cbuf[cpos] = c;
   1112   1.1       cgd 				return (NUMBER);
   1113   1.1       cgd 			}
   1114   1.1       cgd 			switch (cbuf[cpos++]) {
   1115   1.1       cgd 
   1116   1.1       cgd 			case '\n':
   1117   1.1       cgd 				state = CMD;
   1118   1.1       cgd 				return (CRLF);
   1119   1.1       cgd 
   1120   1.1       cgd 			case ' ':
   1121   1.1       cgd 				return (SP);
   1122   1.1       cgd 
   1123   1.1       cgd 			case ',':
   1124   1.1       cgd 				return (COMMA);
   1125   1.1       cgd 
   1126   1.1       cgd 			case 'A':
   1127   1.1       cgd 			case 'a':
   1128   1.1       cgd 				return (A);
   1129   1.1       cgd 
   1130   1.1       cgd 			case 'B':
   1131   1.1       cgd 			case 'b':
   1132   1.1       cgd 				return (B);
   1133   1.1       cgd 
   1134   1.1       cgd 			case 'C':
   1135   1.1       cgd 			case 'c':
   1136   1.1       cgd 				return (C);
   1137   1.1       cgd 
   1138   1.1       cgd 			case 'E':
   1139   1.1       cgd 			case 'e':
   1140   1.1       cgd 				return (E);
   1141   1.1       cgd 
   1142   1.1       cgd 			case 'F':
   1143   1.1       cgd 			case 'f':
   1144   1.1       cgd 				return (F);
   1145   1.1       cgd 
   1146   1.1       cgd 			case 'I':
   1147   1.1       cgd 			case 'i':
   1148   1.1       cgd 				return (I);
   1149   1.1       cgd 
   1150   1.1       cgd 			case 'L':
   1151   1.1       cgd 			case 'l':
   1152   1.1       cgd 				return (L);
   1153   1.1       cgd 
   1154   1.1       cgd 			case 'N':
   1155   1.1       cgd 			case 'n':
   1156   1.1       cgd 				return (N);
   1157   1.1       cgd 
   1158   1.1       cgd 			case 'P':
   1159   1.1       cgd 			case 'p':
   1160   1.1       cgd 				return (P);
   1161   1.1       cgd 
   1162   1.1       cgd 			case 'R':
   1163   1.1       cgd 			case 'r':
   1164   1.1       cgd 				return (R);
   1165   1.1       cgd 
   1166   1.1       cgd 			case 'S':
   1167   1.1       cgd 			case 's':
   1168   1.1       cgd 				return (S);
   1169   1.1       cgd 
   1170   1.1       cgd 			case 'T':
   1171   1.1       cgd 			case 't':
   1172   1.1       cgd 				return (T);
   1173   1.1       cgd 
   1174   1.1       cgd 			}
   1175   1.1       cgd 			break;
   1176   1.1       cgd 
   1177  1.21     lukem 		case NOARGS:
   1178  1.21     lukem 			if (cbuf[cpos] == '\n') {
   1179  1.21     lukem 				state = CMD;
   1180  1.21     lukem 				return (CRLF);
   1181  1.21     lukem 			}
   1182  1.21     lukem 			c = cbuf[cpos];
   1183  1.21     lukem 			cbuf[cpos] = '\0';
   1184  1.21     lukem 			reply(501, "'%s' command does not take any arguments.",
   1185  1.21     lukem 			    cbuf);
   1186  1.21     lukem 			cbuf[cpos] = c;
   1187  1.21     lukem 			errored = 1;
   1188  1.21     lukem 			break;
   1189  1.21     lukem 
   1190   1.1       cgd 		default:
   1191   1.1       cgd 			fatal("Unknown state in scanner.");
   1192   1.1       cgd 		}
   1193  1.21     lukem 		if (!errored)
   1194  1.21     lukem 			yyerror((char *) 0);
   1195   1.1       cgd 		state = CMD;
   1196  1.21     lukem 		longjmp(errcatch, 0);
   1197  1.21     lukem 		/* NOTREACHED */
   1198   1.1       cgd 	}
   1199   1.1       cgd }
   1200   1.1       cgd 
   1201   1.4   deraadt void
   1202   1.1       cgd upper(s)
   1203   1.4   deraadt 	char *s;
   1204   1.1       cgd {
   1205   1.1       cgd 	while (*s != '\0') {
   1206  1.11       cjs 		*s = toupper(*s);
   1207   1.1       cgd 		s++;
   1208   1.1       cgd 	}
   1209   1.1       cgd }
   1210   1.1       cgd 
   1211   1.4   deraadt static char *
   1212   1.1       cgd copy(s)
   1213   1.1       cgd 	char *s;
   1214   1.1       cgd {
   1215   1.1       cgd 	char *p;
   1216   1.1       cgd 
   1217  1.21     lukem 	p = strdup(s);
   1218   1.1       cgd 	if (p == NULL)
   1219   1.1       cgd 		fatal("Ran out of memory.");
   1220   1.1       cgd 	return (p);
   1221   1.1       cgd }
   1222   1.1       cgd 
   1223   1.4   deraadt static void
   1224   1.1       cgd help(ctab, s)
   1225   1.1       cgd 	struct tab *ctab;
   1226   1.1       cgd 	char *s;
   1227   1.1       cgd {
   1228   1.4   deraadt 	struct tab *c;
   1229   1.4   deraadt 	int width, NCMDS;
   1230   1.1       cgd 	char *type;
   1231   1.1       cgd 
   1232   1.1       cgd 	if (ctab == sitetab)
   1233   1.1       cgd 		type = "SITE ";
   1234   1.1       cgd 	else
   1235   1.1       cgd 		type = "";
   1236   1.1       cgd 	width = 0, NCMDS = 0;
   1237   1.1       cgd 	for (c = ctab; c->name != NULL; c++) {
   1238   1.1       cgd 		int len = strlen(c->name);
   1239   1.1       cgd 
   1240   1.1       cgd 		if (len > width)
   1241   1.1       cgd 			width = len;
   1242   1.1       cgd 		NCMDS++;
   1243   1.1       cgd 	}
   1244   1.1       cgd 	width = (width + 8) &~ 7;
   1245   1.1       cgd 	if (s == 0) {
   1246   1.4   deraadt 		int i, j, w;
   1247   1.1       cgd 		int columns, lines;
   1248   1.1       cgd 
   1249   1.1       cgd 		lreply(214, "The following %scommands are recognized %s.",
   1250   1.1       cgd 		    type, "(* =>'s unimplemented)");
   1251   1.1       cgd 		columns = 76 / width;
   1252   1.1       cgd 		if (columns == 0)
   1253   1.1       cgd 			columns = 1;
   1254   1.1       cgd 		lines = (NCMDS + columns - 1) / columns;
   1255   1.1       cgd 		for (i = 0; i < lines; i++) {
   1256   1.1       cgd 			printf("   ");
   1257   1.1       cgd 			for (j = 0; j < columns; j++) {
   1258   1.1       cgd 				c = ctab + j * lines + i;
   1259   1.1       cgd 				printf("%s%c", c->name,
   1260   1.1       cgd 					c->implemented ? ' ' : '*');
   1261   1.1       cgd 				if (c + lines >= &ctab[NCMDS])
   1262   1.1       cgd 					break;
   1263   1.1       cgd 				w = strlen(c->name) + 1;
   1264   1.1       cgd 				while (w < width) {
   1265   1.1       cgd 					putchar(' ');
   1266   1.1       cgd 					w++;
   1267   1.1       cgd 				}
   1268   1.1       cgd 			}
   1269   1.1       cgd 			printf("\r\n");
   1270   1.1       cgd 		}
   1271   1.1       cgd 		(void) fflush(stdout);
   1272   1.1       cgd 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
   1273   1.1       cgd 		return;
   1274   1.1       cgd 	}
   1275   1.1       cgd 	upper(s);
   1276   1.1       cgd 	c = lookup(ctab, s);
   1277   1.1       cgd 	if (c == (struct tab *)0) {
   1278   1.1       cgd 		reply(502, "Unknown command %s.", s);
   1279   1.1       cgd 		return;
   1280   1.1       cgd 	}
   1281   1.1       cgd 	if (c->implemented)
   1282   1.1       cgd 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
   1283   1.1       cgd 	else
   1284   1.1       cgd 		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
   1285   1.1       cgd 		    c->name, c->help);
   1286   1.1       cgd }
   1287   1.1       cgd 
   1288   1.4   deraadt static void
   1289   1.1       cgd sizecmd(filename)
   1290   1.4   deraadt 	char *filename;
   1291   1.1       cgd {
   1292   1.1       cgd 	switch (type) {
   1293   1.1       cgd 	case TYPE_L:
   1294   1.1       cgd 	case TYPE_I: {
   1295   1.1       cgd 		struct stat stbuf;
   1296   1.4   deraadt 		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
   1297   1.1       cgd 			reply(550, "%s: not a plain file.", filename);
   1298   1.1       cgd 		else
   1299   1.4   deraadt 			reply(213, "%qu", stbuf.st_size);
   1300   1.4   deraadt 		break; }
   1301   1.1       cgd 	case TYPE_A: {
   1302   1.1       cgd 		FILE *fin;
   1303   1.4   deraadt 		int c;
   1304   1.4   deraadt 		off_t count;
   1305   1.1       cgd 		struct stat stbuf;
   1306   1.1       cgd 		fin = fopen(filename, "r");
   1307   1.1       cgd 		if (fin == NULL) {
   1308   1.1       cgd 			perror_reply(550, filename);
   1309   1.1       cgd 			return;
   1310   1.1       cgd 		}
   1311   1.4   deraadt 		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
   1312   1.1       cgd 			reply(550, "%s: not a plain file.", filename);
   1313   1.1       cgd 			(void) fclose(fin);
   1314   1.1       cgd 			return;
   1315   1.1       cgd 		}
   1316   1.1       cgd 
   1317   1.1       cgd 		count = 0;
   1318   1.1       cgd 		while((c=getc(fin)) != EOF) {
   1319   1.1       cgd 			if (c == '\n')	/* will get expanded to \r\n */
   1320   1.1       cgd 				count++;
   1321   1.1       cgd 			count++;
   1322   1.1       cgd 		}
   1323   1.1       cgd 		(void) fclose(fin);
   1324   1.1       cgd 
   1325   1.4   deraadt 		reply(213, "%qd", count);
   1326   1.4   deraadt 		break; }
   1327   1.1       cgd 	default:
   1328   1.1       cgd 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
   1329   1.1       cgd 	}
   1330   1.1       cgd }
   1331