Home | History | Annotate | Line # | Download | only in ftpd
ftpcmd.y revision 1.1
      1  1.1  cgd /*
      2  1.1  cgd  * Copyright (c) 1985, 1988 Regents of the University of California.
      3  1.1  cgd  * All rights reserved.
      4  1.1  cgd  *
      5  1.1  cgd  * Redistribution and use in source and binary forms, with or without
      6  1.1  cgd  * modification, are permitted provided that the following conditions
      7  1.1  cgd  * are met:
      8  1.1  cgd  * 1. Redistributions of source code must retain the above copyright
      9  1.1  cgd  *    notice, this list of conditions and the following disclaimer.
     10  1.1  cgd  * 2. Redistributions in binary form must reproduce the above copyright
     11  1.1  cgd  *    notice, this list of conditions and the following disclaimer in the
     12  1.1  cgd  *    documentation and/or other materials provided with the distribution.
     13  1.1  cgd  * 3. All advertising materials mentioning features or use of this software
     14  1.1  cgd  *    must display the following acknowledgement:
     15  1.1  cgd  *	This product includes software developed by the University of
     16  1.1  cgd  *	California, Berkeley and its contributors.
     17  1.1  cgd  * 4. Neither the name of the University nor the names of its contributors
     18  1.1  cgd  *    may be used to endorse or promote products derived from this software
     19  1.1  cgd  *    without specific prior written permission.
     20  1.1  cgd  *
     21  1.1  cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  1.1  cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  1.1  cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  1.1  cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  1.1  cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  1.1  cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  1.1  cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  1.1  cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  1.1  cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  1.1  cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  1.1  cgd  * SUCH DAMAGE.
     32  1.1  cgd  *
     33  1.1  cgd  *	@(#)ftpcmd.y	5.24 (Berkeley) 2/25/91
     34  1.1  cgd  */
     35  1.1  cgd 
     36  1.1  cgd /*
     37  1.1  cgd  * Grammar for FTP commands.
     38  1.1  cgd  * See RFC 959.
     39  1.1  cgd  */
     40  1.1  cgd 
     41  1.1  cgd %{
     42  1.1  cgd 
     43  1.1  cgd #ifndef lint
     44  1.1  cgd static char sccsid[] = "@(#)ftpcmd.y	5.24 (Berkeley) 2/25/91";
     45  1.1  cgd #endif /* not lint */
     46  1.1  cgd 
     47  1.1  cgd #include <sys/param.h>
     48  1.1  cgd #include <sys/socket.h>
     49  1.1  cgd #include <sys/stat.h>
     50  1.1  cgd #include <netinet/in.h>
     51  1.1  cgd #include <arpa/ftp.h>
     52  1.1  cgd #include <signal.h>
     53  1.1  cgd #include <setjmp.h>
     54  1.1  cgd #include <syslog.h>
     55  1.1  cgd #include <time.h>
     56  1.1  cgd #include <pwd.h>
     57  1.1  cgd #include <unistd.h>
     58  1.1  cgd #include <stdio.h>
     59  1.1  cgd #include <ctype.h>
     60  1.1  cgd #include <stdlib.h>
     61  1.1  cgd #include <string.h>
     62  1.1  cgd 
     63  1.1  cgd extern	struct sockaddr_in data_dest;
     64  1.1  cgd extern	int logged_in;
     65  1.1  cgd extern	struct passwd *pw;
     66  1.1  cgd extern	int guest;
     67  1.1  cgd extern	int logging;
     68  1.1  cgd extern	int type;
     69  1.1  cgd extern	int form;
     70  1.1  cgd extern	int debug;
     71  1.1  cgd extern	int timeout;
     72  1.1  cgd extern	int maxtimeout;
     73  1.1  cgd extern  int pdata;
     74  1.1  cgd extern	char hostname[], remotehost[];
     75  1.1  cgd extern	char proctitle[];
     76  1.1  cgd extern	char *globerr;
     77  1.1  cgd extern	int usedefault;
     78  1.1  cgd extern  int transflag;
     79  1.1  cgd extern  char tmpline[];
     80  1.1  cgd char	**ftpglob();
     81  1.1  cgd 
     82  1.1  cgd off_t	restart_point;
     83  1.1  cgd 
     84  1.1  cgd static	int cmd_type;
     85  1.1  cgd static	int cmd_form;
     86  1.1  cgd static	int cmd_bytesz;
     87  1.1  cgd char	cbuf[512];
     88  1.1  cgd char	*fromname;
     89  1.1  cgd 
     90  1.1  cgd %}
     91  1.1  cgd 
     92  1.1  cgd %token
     93  1.1  cgd 	A	B	C	E	F	I
     94  1.1  cgd 	L	N	P	R	S	T
     95  1.1  cgd 
     96  1.1  cgd 	SP	CRLF	COMMA	STRING	NUMBER
     97  1.1  cgd 
     98  1.1  cgd 	USER	PASS	ACCT	REIN	QUIT	PORT
     99  1.1  cgd 	PASV	TYPE	STRU	MODE	RETR	STOR
    100  1.1  cgd 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
    101  1.1  cgd 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
    102  1.1  cgd 	ABOR	DELE	CWD	LIST	NLST	SITE
    103  1.1  cgd 	STAT	HELP	NOOP	MKD	RMD	PWD
    104  1.1  cgd 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
    105  1.1  cgd 
    106  1.1  cgd 	UMASK	IDLE	CHMOD
    107  1.1  cgd 
    108  1.1  cgd 	LEXERR
    109  1.1  cgd 
    110  1.1  cgd %start	cmd_list
    111  1.1  cgd 
    112  1.1  cgd %%
    113  1.1  cgd 
    114  1.1  cgd cmd_list:	/* empty */
    115  1.1  cgd 	|	cmd_list cmd
    116  1.1  cgd 		= {
    117  1.1  cgd 			fromname = (char *) 0;
    118  1.1  cgd 			restart_point = (off_t) 0;
    119  1.1  cgd 		}
    120  1.1  cgd 	|	cmd_list rcmd
    121  1.1  cgd 	;
    122  1.1  cgd 
    123  1.1  cgd cmd:		USER SP username CRLF
    124  1.1  cgd 		= {
    125  1.1  cgd 			user((char *) $3);
    126  1.1  cgd 			free((char *) $3);
    127  1.1  cgd 		}
    128  1.1  cgd 	|	PASS SP password CRLF
    129  1.1  cgd 		= {
    130  1.1  cgd 			pass((char *) $3);
    131  1.1  cgd 			free((char *) $3);
    132  1.1  cgd 		}
    133  1.1  cgd 	|	PORT SP host_port CRLF
    134  1.1  cgd 		= {
    135  1.1  cgd 			usedefault = 0;
    136  1.1  cgd 			if (pdata >= 0) {
    137  1.1  cgd 				(void) close(pdata);
    138  1.1  cgd 				pdata = -1;
    139  1.1  cgd 			}
    140  1.1  cgd 			reply(200, "PORT command successful.");
    141  1.1  cgd 		}
    142  1.1  cgd 	|	PASV CRLF
    143  1.1  cgd 		= {
    144  1.1  cgd 			passive();
    145  1.1  cgd 		}
    146  1.1  cgd 	|	TYPE SP type_code CRLF
    147  1.1  cgd 		= {
    148  1.1  cgd 			switch (cmd_type) {
    149  1.1  cgd 
    150  1.1  cgd 			case TYPE_A:
    151  1.1  cgd 				if (cmd_form == FORM_N) {
    152  1.1  cgd 					reply(200, "Type set to A.");
    153  1.1  cgd 					type = cmd_type;
    154  1.1  cgd 					form = cmd_form;
    155  1.1  cgd 				} else
    156  1.1  cgd 					reply(504, "Form must be N.");
    157  1.1  cgd 				break;
    158  1.1  cgd 
    159  1.1  cgd 			case TYPE_E:
    160  1.1  cgd 				reply(504, "Type E not implemented.");
    161  1.1  cgd 				break;
    162  1.1  cgd 
    163  1.1  cgd 			case TYPE_I:
    164  1.1  cgd 				reply(200, "Type set to I.");
    165  1.1  cgd 				type = cmd_type;
    166  1.1  cgd 				break;
    167  1.1  cgd 
    168  1.1  cgd 			case TYPE_L:
    169  1.1  cgd #if NBBY == 8
    170  1.1  cgd 				if (cmd_bytesz == 8) {
    171  1.1  cgd 					reply(200,
    172  1.1  cgd 					    "Type set to L (byte size 8).");
    173  1.1  cgd 					type = cmd_type;
    174  1.1  cgd 				} else
    175  1.1  cgd 					reply(504, "Byte size must be 8.");
    176  1.1  cgd #else /* NBBY == 8 */
    177  1.1  cgd 				UNIMPLEMENTED for NBBY != 8
    178  1.1  cgd #endif /* NBBY == 8 */
    179  1.1  cgd 			}
    180  1.1  cgd 		}
    181  1.1  cgd 	|	STRU SP struct_code CRLF
    182  1.1  cgd 		= {
    183  1.1  cgd 			switch ($3) {
    184  1.1  cgd 
    185  1.1  cgd 			case STRU_F:
    186  1.1  cgd 				reply(200, "STRU F ok.");
    187  1.1  cgd 				break;
    188  1.1  cgd 
    189  1.1  cgd 			default:
    190  1.1  cgd 				reply(504, "Unimplemented STRU type.");
    191  1.1  cgd 			}
    192  1.1  cgd 		}
    193  1.1  cgd 	|	MODE SP mode_code CRLF
    194  1.1  cgd 		= {
    195  1.1  cgd 			switch ($3) {
    196  1.1  cgd 
    197  1.1  cgd 			case MODE_S:
    198  1.1  cgd 				reply(200, "MODE S ok.");
    199  1.1  cgd 				break;
    200  1.1  cgd 
    201  1.1  cgd 			default:
    202  1.1  cgd 				reply(502, "Unimplemented MODE type.");
    203  1.1  cgd 			}
    204  1.1  cgd 		}
    205  1.1  cgd 	|	ALLO SP NUMBER CRLF
    206  1.1  cgd 		= {
    207  1.1  cgd 			reply(202, "ALLO command ignored.");
    208  1.1  cgd 		}
    209  1.1  cgd 	|	ALLO SP NUMBER SP R SP NUMBER CRLF
    210  1.1  cgd 		= {
    211  1.1  cgd 			reply(202, "ALLO command ignored.");
    212  1.1  cgd 		}
    213  1.1  cgd 	|	RETR check_login SP pathname CRLF
    214  1.1  cgd 		= {
    215  1.1  cgd 			if ($2 && $4 != NULL)
    216  1.1  cgd 				retrieve((char *) 0, (char *) $4);
    217  1.1  cgd 			if ($4 != NULL)
    218  1.1  cgd 				free((char *) $4);
    219  1.1  cgd 		}
    220  1.1  cgd 	|	STOR check_login SP pathname CRLF
    221  1.1  cgd 		= {
    222  1.1  cgd 			if ($2 && $4 != NULL)
    223  1.1  cgd 				store((char *) $4, "w", 0);
    224  1.1  cgd 			if ($4 != NULL)
    225  1.1  cgd 				free((char *) $4);
    226  1.1  cgd 		}
    227  1.1  cgd 	|	APPE check_login SP pathname CRLF
    228  1.1  cgd 		= {
    229  1.1  cgd 			if ($2 && $4 != NULL)
    230  1.1  cgd 				store((char *) $4, "a", 0);
    231  1.1  cgd 			if ($4 != NULL)
    232  1.1  cgd 				free((char *) $4);
    233  1.1  cgd 		}
    234  1.1  cgd 	|	NLST check_login CRLF
    235  1.1  cgd 		= {
    236  1.1  cgd 			if ($2)
    237  1.1  cgd 				send_file_list(".");
    238  1.1  cgd 		}
    239  1.1  cgd 	|	NLST check_login SP STRING CRLF
    240  1.1  cgd 		= {
    241  1.1  cgd 			if ($2 && $4 != NULL)
    242  1.1  cgd 				send_file_list((char *) $4);
    243  1.1  cgd 			if ($4 != NULL)
    244  1.1  cgd 				free((char *) $4);
    245  1.1  cgd 		}
    246  1.1  cgd 	|	LIST check_login CRLF
    247  1.1  cgd 		= {
    248  1.1  cgd 			if ($2)
    249  1.1  cgd 				retrieve("/bin/ls -lgA", "");
    250  1.1  cgd 		}
    251  1.1  cgd 	|	LIST check_login SP pathname CRLF
    252  1.1  cgd 		= {
    253  1.1  cgd 			if ($2 && $4 != NULL)
    254  1.1  cgd 				retrieve("/bin/ls -lgA %s", (char *) $4);
    255  1.1  cgd 			if ($4 != NULL)
    256  1.1  cgd 				free((char *) $4);
    257  1.1  cgd 		}
    258  1.1  cgd 	|	STAT check_login SP pathname CRLF
    259  1.1  cgd 		= {
    260  1.1  cgd 			if ($2 && $4 != NULL)
    261  1.1  cgd 				statfilecmd((char *) $4);
    262  1.1  cgd 			if ($4 != NULL)
    263  1.1  cgd 				free((char *) $4);
    264  1.1  cgd 		}
    265  1.1  cgd 	|	STAT CRLF
    266  1.1  cgd 		= {
    267  1.1  cgd 			statcmd();
    268  1.1  cgd 		}
    269  1.1  cgd 	|	DELE check_login SP pathname CRLF
    270  1.1  cgd 		= {
    271  1.1  cgd 			if ($2 && $4 != NULL)
    272  1.1  cgd 				delete((char *) $4);
    273  1.1  cgd 			if ($4 != NULL)
    274  1.1  cgd 				free((char *) $4);
    275  1.1  cgd 		}
    276  1.1  cgd 	|	RNTO SP pathname CRLF
    277  1.1  cgd 		= {
    278  1.1  cgd 			if (fromname) {
    279  1.1  cgd 				renamecmd(fromname, (char *) $3);
    280  1.1  cgd 				free(fromname);
    281  1.1  cgd 				fromname = (char *) 0;
    282  1.1  cgd 			} else {
    283  1.1  cgd 				reply(503, "Bad sequence of commands.");
    284  1.1  cgd 			}
    285  1.1  cgd 			free((char *) $3);
    286  1.1  cgd 		}
    287  1.1  cgd 	|	ABOR CRLF
    288  1.1  cgd 		= {
    289  1.1  cgd 			reply(225, "ABOR command successful.");
    290  1.1  cgd 		}
    291  1.1  cgd 	|	CWD check_login CRLF
    292  1.1  cgd 		= {
    293  1.1  cgd 			if ($2)
    294  1.1  cgd 				cwd(pw->pw_dir);
    295  1.1  cgd 		}
    296  1.1  cgd 	|	CWD check_login SP pathname CRLF
    297  1.1  cgd 		= {
    298  1.1  cgd 			if ($2 && $4 != NULL)
    299  1.1  cgd 				cwd((char *) $4);
    300  1.1  cgd 			if ($4 != NULL)
    301  1.1  cgd 				free((char *) $4);
    302  1.1  cgd 		}
    303  1.1  cgd 	|	HELP CRLF
    304  1.1  cgd 		= {
    305  1.1  cgd 			help(cmdtab, (char *) 0);
    306  1.1  cgd 		}
    307  1.1  cgd 	|	HELP SP STRING CRLF
    308  1.1  cgd 		= {
    309  1.1  cgd 			register char *cp = (char *)$3;
    310  1.1  cgd 
    311  1.1  cgd 			if (strncasecmp(cp, "SITE", 4) == 0) {
    312  1.1  cgd 				cp = (char *)$3 + 4;
    313  1.1  cgd 				if (*cp == ' ')
    314  1.1  cgd 					cp++;
    315  1.1  cgd 				if (*cp)
    316  1.1  cgd 					help(sitetab, cp);
    317  1.1  cgd 				else
    318  1.1  cgd 					help(sitetab, (char *) 0);
    319  1.1  cgd 			} else
    320  1.1  cgd 				help(cmdtab, (char *) $3);
    321  1.1  cgd 		}
    322  1.1  cgd 	|	NOOP CRLF
    323  1.1  cgd 		= {
    324  1.1  cgd 			reply(200, "NOOP command successful.");
    325  1.1  cgd 		}
    326  1.1  cgd 	|	MKD check_login SP pathname CRLF
    327  1.1  cgd 		= {
    328  1.1  cgd 			if ($2 && $4 != NULL)
    329  1.1  cgd 				makedir((char *) $4);
    330  1.1  cgd 			if ($4 != NULL)
    331  1.1  cgd 				free((char *) $4);
    332  1.1  cgd 		}
    333  1.1  cgd 	|	RMD check_login SP pathname CRLF
    334  1.1  cgd 		= {
    335  1.1  cgd 			if ($2 && $4 != NULL)
    336  1.1  cgd 				removedir((char *) $4);
    337  1.1  cgd 			if ($4 != NULL)
    338  1.1  cgd 				free((char *) $4);
    339  1.1  cgd 		}
    340  1.1  cgd 	|	PWD check_login CRLF
    341  1.1  cgd 		= {
    342  1.1  cgd 			if ($2)
    343  1.1  cgd 				pwd();
    344  1.1  cgd 		}
    345  1.1  cgd 	|	CDUP check_login CRLF
    346  1.1  cgd 		= {
    347  1.1  cgd 			if ($2)
    348  1.1  cgd 				cwd("..");
    349  1.1  cgd 		}
    350  1.1  cgd 	|	SITE SP HELP CRLF
    351  1.1  cgd 		= {
    352  1.1  cgd 			help(sitetab, (char *) 0);
    353  1.1  cgd 		}
    354  1.1  cgd 	|	SITE SP HELP SP STRING CRLF
    355  1.1  cgd 		= {
    356  1.1  cgd 			help(sitetab, (char *) $5);
    357  1.1  cgd 		}
    358  1.1  cgd 	|	SITE SP UMASK check_login CRLF
    359  1.1  cgd 		= {
    360  1.1  cgd 			int oldmask;
    361  1.1  cgd 
    362  1.1  cgd 			if ($4) {
    363  1.1  cgd 				oldmask = umask(0);
    364  1.1  cgd 				(void) umask(oldmask);
    365  1.1  cgd 				reply(200, "Current UMASK is %03o", oldmask);
    366  1.1  cgd 			}
    367  1.1  cgd 		}
    368  1.1  cgd 	|	SITE SP UMASK check_login SP octal_number CRLF
    369  1.1  cgd 		= {
    370  1.1  cgd 			int oldmask;
    371  1.1  cgd 
    372  1.1  cgd 			if ($4) {
    373  1.1  cgd 				if (($6 == -1) || ($6 > 0777)) {
    374  1.1  cgd 					reply(501, "Bad UMASK value");
    375  1.1  cgd 				} else {
    376  1.1  cgd 					oldmask = umask($6);
    377  1.1  cgd 					reply(200,
    378  1.1  cgd 					    "UMASK set to %03o (was %03o)",
    379  1.1  cgd 					    $6, oldmask);
    380  1.1  cgd 				}
    381  1.1  cgd 			}
    382  1.1  cgd 		}
    383  1.1  cgd 	|	SITE SP CHMOD check_login SP octal_number SP pathname CRLF
    384  1.1  cgd 		= {
    385  1.1  cgd 			if ($4 && ($8 != NULL)) {
    386  1.1  cgd 				if ($6 > 0777)
    387  1.1  cgd 					reply(501,
    388  1.1  cgd 				"CHMOD: Mode value must be between 0 and 0777");
    389  1.1  cgd 				else if (chmod((char *) $8, $6) < 0)
    390  1.1  cgd 					perror_reply(550, (char *) $8);
    391  1.1  cgd 				else
    392  1.1  cgd 					reply(200, "CHMOD command successful.");
    393  1.1  cgd 			}
    394  1.1  cgd 			if ($8 != NULL)
    395  1.1  cgd 				free((char *) $8);
    396  1.1  cgd 		}
    397  1.1  cgd 	|	SITE SP IDLE CRLF
    398  1.1  cgd 		= {
    399  1.1  cgd 			reply(200,
    400  1.1  cgd 			    "Current IDLE time limit is %d seconds; max %d",
    401  1.1  cgd 				timeout, maxtimeout);
    402  1.1  cgd 		}
    403  1.1  cgd 	|	SITE SP IDLE SP NUMBER CRLF
    404  1.1  cgd 		= {
    405  1.1  cgd 			if ($5 < 30 || $5 > maxtimeout) {
    406  1.1  cgd 				reply(501,
    407  1.1  cgd 			"Maximum IDLE time must be between 30 and %d seconds",
    408  1.1  cgd 				    maxtimeout);
    409  1.1  cgd 			} else {
    410  1.1  cgd 				timeout = $5;
    411  1.1  cgd 				(void) alarm((unsigned) timeout);
    412  1.1  cgd 				reply(200,
    413  1.1  cgd 				    "Maximum IDLE time set to %d seconds",
    414  1.1  cgd 				    timeout);
    415  1.1  cgd 			}
    416  1.1  cgd 		}
    417  1.1  cgd 	|	STOU check_login SP pathname CRLF
    418  1.1  cgd 		= {
    419  1.1  cgd 			if ($2 && $4 != NULL)
    420  1.1  cgd 				store((char *) $4, "w", 1);
    421  1.1  cgd 			if ($4 != NULL)
    422  1.1  cgd 				free((char *) $4);
    423  1.1  cgd 		}
    424  1.1  cgd 	|	SYST CRLF
    425  1.1  cgd 		= {
    426  1.1  cgd #ifdef unix
    427  1.1  cgd #ifdef BSD
    428  1.1  cgd 			reply(215, "UNIX Type: L%d Version: BSD-%d",
    429  1.1  cgd 				NBBY, BSD);
    430  1.1  cgd #else /* BSD */
    431  1.1  cgd 			reply(215, "UNIX Type: L%d", NBBY);
    432  1.1  cgd #endif /* BSD */
    433  1.1  cgd #else /* unix */
    434  1.1  cgd 			reply(215, "UNKNOWN Type: L%d", NBBY);
    435  1.1  cgd #endif /* unix */
    436  1.1  cgd 		}
    437  1.1  cgd 
    438  1.1  cgd 		/*
    439  1.1  cgd 		 * SIZE is not in RFC959, but Postel has blessed it and
    440  1.1  cgd 		 * it will be in the updated RFC.
    441  1.1  cgd 		 *
    442  1.1  cgd 		 * Return size of file in a format suitable for
    443  1.1  cgd 		 * using with RESTART (we just count bytes).
    444  1.1  cgd 		 */
    445  1.1  cgd 	|	SIZE check_login SP pathname CRLF
    446  1.1  cgd 		= {
    447  1.1  cgd 			if ($2 && $4 != NULL)
    448  1.1  cgd 				sizecmd((char *) $4);
    449  1.1  cgd 			if ($4 != NULL)
    450  1.1  cgd 				free((char *) $4);
    451  1.1  cgd 		}
    452  1.1  cgd 
    453  1.1  cgd 		/*
    454  1.1  cgd 		 * MDTM is not in RFC959, but Postel has blessed it and
    455  1.1  cgd 		 * it will be in the updated RFC.
    456  1.1  cgd 		 *
    457  1.1  cgd 		 * Return modification time of file as an ISO 3307
    458  1.1  cgd 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
    459  1.1  cgd 		 * where xxx is the fractional second (of any precision,
    460  1.1  cgd 		 * not necessarily 3 digits)
    461  1.1  cgd 		 */
    462  1.1  cgd 	|	MDTM check_login SP pathname CRLF
    463  1.1  cgd 		= {
    464  1.1  cgd 			if ($2 && $4 != NULL) {
    465  1.1  cgd 				struct stat stbuf;
    466  1.1  cgd 				if (stat((char *) $4, &stbuf) < 0)
    467  1.1  cgd 					perror_reply(550, "%s", (char *) $4);
    468  1.1  cgd 				else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
    469  1.1  cgd 					reply(550, "%s: not a plain file.",
    470  1.1  cgd 						(char *) $4);
    471  1.1  cgd 				} else {
    472  1.1  cgd 					register struct tm *t;
    473  1.1  cgd 					struct tm *gmtime();
    474  1.1  cgd 					t = gmtime(&stbuf.st_mtime);
    475  1.1  cgd 					reply(213,
    476  1.1  cgd 					    "19%02d%02d%02d%02d%02d%02d",
    477  1.1  cgd 					    t->tm_year, t->tm_mon+1, t->tm_mday,
    478  1.1  cgd 					    t->tm_hour, t->tm_min, t->tm_sec);
    479  1.1  cgd 				}
    480  1.1  cgd 			}
    481  1.1  cgd 			if ($4 != NULL)
    482  1.1  cgd 				free((char *) $4);
    483  1.1  cgd 		}
    484  1.1  cgd 	|	QUIT CRLF
    485  1.1  cgd 		= {
    486  1.1  cgd 			reply(221, "Goodbye.");
    487  1.1  cgd 			dologout(0);
    488  1.1  cgd 		}
    489  1.1  cgd 	|	error CRLF
    490  1.1  cgd 		= {
    491  1.1  cgd 			yyerrok;
    492  1.1  cgd 		}
    493  1.1  cgd 	;
    494  1.1  cgd rcmd:		RNFR check_login SP pathname CRLF
    495  1.1  cgd 		= {
    496  1.1  cgd 			char *renamefrom();
    497  1.1  cgd 
    498  1.1  cgd 			restart_point = (off_t) 0;
    499  1.1  cgd 			if ($2 && $4) {
    500  1.1  cgd 				fromname = renamefrom((char *) $4);
    501  1.1  cgd 				if (fromname == (char *) 0 && $4) {
    502  1.1  cgd 					free((char *) $4);
    503  1.1  cgd 				}
    504  1.1  cgd 			}
    505  1.1  cgd 		}
    506  1.1  cgd 	|	REST SP byte_size CRLF
    507  1.1  cgd 		= {
    508  1.1  cgd 			long atol();
    509  1.1  cgd 
    510  1.1  cgd 			fromname = (char *) 0;
    511  1.1  cgd 			restart_point = $3;
    512  1.1  cgd 			reply(350, "Restarting at %ld. %s", restart_point,
    513  1.1  cgd 			    "Send STORE or RETRIEVE to initiate transfer.");
    514  1.1  cgd 		}
    515  1.1  cgd 	;
    516  1.1  cgd 
    517  1.1  cgd username:	STRING
    518  1.1  cgd 	;
    519  1.1  cgd 
    520  1.1  cgd password:	/* empty */
    521  1.1  cgd 		= {
    522  1.1  cgd 			*(char **)&($$) = (char *)calloc(1, sizeof(char));
    523  1.1  cgd 		}
    524  1.1  cgd 	|	STRING
    525  1.1  cgd 	;
    526  1.1  cgd 
    527  1.1  cgd byte_size:	NUMBER
    528  1.1  cgd 	;
    529  1.1  cgd 
    530  1.1  cgd host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    531  1.1  cgd 		NUMBER COMMA NUMBER
    532  1.1  cgd 		= {
    533  1.1  cgd 			register char *a, *p;
    534  1.1  cgd 
    535  1.1  cgd 			a = (char *)&data_dest.sin_addr;
    536  1.1  cgd 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
    537  1.1  cgd 			p = (char *)&data_dest.sin_port;
    538  1.1  cgd 			p[0] = $9; p[1] = $11;
    539  1.1  cgd 			data_dest.sin_family = AF_INET;
    540  1.1  cgd 		}
    541  1.1  cgd 	;
    542  1.1  cgd 
    543  1.1  cgd form_code:	N
    544  1.1  cgd 	= {
    545  1.1  cgd 		$$ = FORM_N;
    546  1.1  cgd 	}
    547  1.1  cgd 	|	T
    548  1.1  cgd 	= {
    549  1.1  cgd 		$$ = FORM_T;
    550  1.1  cgd 	}
    551  1.1  cgd 	|	C
    552  1.1  cgd 	= {
    553  1.1  cgd 		$$ = FORM_C;
    554  1.1  cgd 	}
    555  1.1  cgd 	;
    556  1.1  cgd 
    557  1.1  cgd type_code:	A
    558  1.1  cgd 	= {
    559  1.1  cgd 		cmd_type = TYPE_A;
    560  1.1  cgd 		cmd_form = FORM_N;
    561  1.1  cgd 	}
    562  1.1  cgd 	|	A SP form_code
    563  1.1  cgd 	= {
    564  1.1  cgd 		cmd_type = TYPE_A;
    565  1.1  cgd 		cmd_form = $3;
    566  1.1  cgd 	}
    567  1.1  cgd 	|	E
    568  1.1  cgd 	= {
    569  1.1  cgd 		cmd_type = TYPE_E;
    570  1.1  cgd 		cmd_form = FORM_N;
    571  1.1  cgd 	}
    572  1.1  cgd 	|	E SP form_code
    573  1.1  cgd 	= {
    574  1.1  cgd 		cmd_type = TYPE_E;
    575  1.1  cgd 		cmd_form = $3;
    576  1.1  cgd 	}
    577  1.1  cgd 	|	I
    578  1.1  cgd 	= {
    579  1.1  cgd 		cmd_type = TYPE_I;
    580  1.1  cgd 	}
    581  1.1  cgd 	|	L
    582  1.1  cgd 	= {
    583  1.1  cgd 		cmd_type = TYPE_L;
    584  1.1  cgd 		cmd_bytesz = NBBY;
    585  1.1  cgd 	}
    586  1.1  cgd 	|	L SP byte_size
    587  1.1  cgd 	= {
    588  1.1  cgd 		cmd_type = TYPE_L;
    589  1.1  cgd 		cmd_bytesz = $3;
    590  1.1  cgd 	}
    591  1.1  cgd 	/* this is for a bug in the BBN ftp */
    592  1.1  cgd 	|	L byte_size
    593  1.1  cgd 	= {
    594  1.1  cgd 		cmd_type = TYPE_L;
    595  1.1  cgd 		cmd_bytesz = $2;
    596  1.1  cgd 	}
    597  1.1  cgd 	;
    598  1.1  cgd 
    599  1.1  cgd struct_code:	F
    600  1.1  cgd 	= {
    601  1.1  cgd 		$$ = STRU_F;
    602  1.1  cgd 	}
    603  1.1  cgd 	|	R
    604  1.1  cgd 	= {
    605  1.1  cgd 		$$ = STRU_R;
    606  1.1  cgd 	}
    607  1.1  cgd 	|	P
    608  1.1  cgd 	= {
    609  1.1  cgd 		$$ = STRU_P;
    610  1.1  cgd 	}
    611  1.1  cgd 	;
    612  1.1  cgd 
    613  1.1  cgd mode_code:	S
    614  1.1  cgd 	= {
    615  1.1  cgd 		$$ = MODE_S;
    616  1.1  cgd 	}
    617  1.1  cgd 	|	B
    618  1.1  cgd 	= {
    619  1.1  cgd 		$$ = MODE_B;
    620  1.1  cgd 	}
    621  1.1  cgd 	|	C
    622  1.1  cgd 	= {
    623  1.1  cgd 		$$ = MODE_C;
    624  1.1  cgd 	}
    625  1.1  cgd 	;
    626  1.1  cgd 
    627  1.1  cgd pathname:	pathstring
    628  1.1  cgd 	= {
    629  1.1  cgd 		/*
    630  1.1  cgd 		 * Problem: this production is used for all pathname
    631  1.1  cgd 		 * processing, but only gives a 550 error reply.
    632  1.1  cgd 		 * This is a valid reply in some cases but not in others.
    633  1.1  cgd 		 */
    634  1.1  cgd 		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
    635  1.1  cgd 			*(char **)&($$) = *ftpglob((char *) $1);
    636  1.1  cgd 			if (globerr != NULL) {
    637  1.1  cgd 				reply(550, globerr);
    638  1.1  cgd 				$$ = NULL;
    639  1.1  cgd 			}
    640  1.1  cgd 			free((char *) $1);
    641  1.1  cgd 		} else
    642  1.1  cgd 			$$ = $1;
    643  1.1  cgd 	}
    644  1.1  cgd 	;
    645  1.1  cgd 
    646  1.1  cgd pathstring:	STRING
    647  1.1  cgd 	;
    648  1.1  cgd 
    649  1.1  cgd octal_number:	NUMBER
    650  1.1  cgd 	= {
    651  1.1  cgd 		register int ret, dec, multby, digit;
    652  1.1  cgd 
    653  1.1  cgd 		/*
    654  1.1  cgd 		 * Convert a number that was read as decimal number
    655  1.1  cgd 		 * to what it would be if it had been read as octal.
    656  1.1  cgd 		 */
    657  1.1  cgd 		dec = $1;
    658  1.1  cgd 		multby = 1;
    659  1.1  cgd 		ret = 0;
    660  1.1  cgd 		while (dec) {
    661  1.1  cgd 			digit = dec%10;
    662  1.1  cgd 			if (digit > 7) {
    663  1.1  cgd 				ret = -1;
    664  1.1  cgd 				break;
    665  1.1  cgd 			}
    666  1.1  cgd 			ret += digit * multby;
    667  1.1  cgd 			multby *= 8;
    668  1.1  cgd 			dec /= 10;
    669  1.1  cgd 		}
    670  1.1  cgd 		$$ = ret;
    671  1.1  cgd 	}
    672  1.1  cgd 	;
    673  1.1  cgd 
    674  1.1  cgd check_login:	/* empty */
    675  1.1  cgd 	= {
    676  1.1  cgd 		if (logged_in)
    677  1.1  cgd 			$$ = 1;
    678  1.1  cgd 		else {
    679  1.1  cgd 			reply(530, "Please login with USER and PASS.");
    680  1.1  cgd 			$$ = 0;
    681  1.1  cgd 		}
    682  1.1  cgd 	}
    683  1.1  cgd 	;
    684  1.1  cgd 
    685  1.1  cgd %%
    686  1.1  cgd 
    687  1.1  cgd extern jmp_buf errcatch;
    688  1.1  cgd 
    689  1.1  cgd #define	CMD	0	/* beginning of command */
    690  1.1  cgd #define	ARGS	1	/* expect miscellaneous arguments */
    691  1.1  cgd #define	STR1	2	/* expect SP followed by STRING */
    692  1.1  cgd #define	STR2	3	/* expect STRING */
    693  1.1  cgd #define	OSTR	4	/* optional SP then STRING */
    694  1.1  cgd #define	ZSTR1	5	/* SP then optional STRING */
    695  1.1  cgd #define	ZSTR2	6	/* optional STRING after SP */
    696  1.1  cgd #define	SITECMD	7	/* SITE command */
    697  1.1  cgd #define	NSTR	8	/* Number followed by a string */
    698  1.1  cgd 
    699  1.1  cgd struct tab {
    700  1.1  cgd 	char	*name;
    701  1.1  cgd 	short	token;
    702  1.1  cgd 	short	state;
    703  1.1  cgd 	short	implemented;	/* 1 if command is implemented */
    704  1.1  cgd 	char	*help;
    705  1.1  cgd };
    706  1.1  cgd 
    707  1.1  cgd struct tab cmdtab[] = {		/* In order defined in RFC 765 */
    708  1.1  cgd 	{ "USER", USER, STR1, 1,	"<sp> username" },
    709  1.1  cgd 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
    710  1.1  cgd 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
    711  1.1  cgd 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
    712  1.1  cgd 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
    713  1.1  cgd 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
    714  1.1  cgd 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
    715  1.1  cgd 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
    716  1.1  cgd 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
    717  1.1  cgd 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
    718  1.1  cgd 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
    719  1.1  cgd 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
    720  1.1  cgd 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
    721  1.1  cgd 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
    722  1.1  cgd 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
    723  1.1  cgd 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
    724  1.1  cgd 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
    725  1.1  cgd 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
    726  1.1  cgd 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
    727  1.1  cgd 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
    728  1.1  cgd 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
    729  1.1  cgd 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
    730  1.1  cgd 	{ "REST", REST, ARGS, 1,	"(restart command)" },
    731  1.1  cgd 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
    732  1.1  cgd 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
    733  1.1  cgd 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
    734  1.1  cgd 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
    735  1.1  cgd 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
    736  1.1  cgd 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
    737  1.1  cgd 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
    738  1.1  cgd 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
    739  1.1  cgd 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
    740  1.1  cgd 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
    741  1.1  cgd 	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
    742  1.1  cgd 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
    743  1.1  cgd 	{ "NOOP", NOOP, ARGS, 1,	"" },
    744  1.1  cgd 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
    745  1.1  cgd 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
    746  1.1  cgd 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
    747  1.1  cgd 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
    748  1.1  cgd 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
    749  1.1  cgd 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
    750  1.1  cgd 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
    751  1.1  cgd 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
    752  1.1  cgd 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
    753  1.1  cgd 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
    754  1.1  cgd 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
    755  1.1  cgd 	{ NULL,   0,    0,    0,	0 }
    756  1.1  cgd };
    757  1.1  cgd 
    758  1.1  cgd struct tab sitetab[] = {
    759  1.1  cgd 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
    760  1.1  cgd 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
    761  1.1  cgd 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
    762  1.1  cgd 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
    763  1.1  cgd 	{ NULL,   0,    0,    0,	0 }
    764  1.1  cgd };
    765  1.1  cgd 
    766  1.1  cgd struct tab *
    767  1.1  cgd lookup(p, cmd)
    768  1.1  cgd 	register struct tab *p;
    769  1.1  cgd 	char *cmd;
    770  1.1  cgd {
    771  1.1  cgd 
    772  1.1  cgd 	for (; p->name != NULL; p++)
    773  1.1  cgd 		if (strcmp(cmd, p->name) == 0)
    774  1.1  cgd 			return (p);
    775  1.1  cgd 	return (0);
    776  1.1  cgd }
    777  1.1  cgd 
    778  1.1  cgd #include <arpa/telnet.h>
    779  1.1  cgd 
    780  1.1  cgd /*
    781  1.1  cgd  * getline - a hacked up version of fgets to ignore TELNET escape codes.
    782  1.1  cgd  */
    783  1.1  cgd char *
    784  1.1  cgd getline(s, n, iop)
    785  1.1  cgd 	char *s;
    786  1.1  cgd 	register FILE *iop;
    787  1.1  cgd {
    788  1.1  cgd 	register c;
    789  1.1  cgd 	register char *cs;
    790  1.1  cgd 
    791  1.1  cgd 	cs = s;
    792  1.1  cgd /* tmpline may contain saved command from urgent mode interruption */
    793  1.1  cgd 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
    794  1.1  cgd 		*cs++ = tmpline[c];
    795  1.1  cgd 		if (tmpline[c] == '\n') {
    796  1.1  cgd 			*cs++ = '\0';
    797  1.1  cgd 			if (debug)
    798  1.1  cgd 				syslog(LOG_DEBUG, "command: %s", s);
    799  1.1  cgd 			tmpline[0] = '\0';
    800  1.1  cgd 			return(s);
    801  1.1  cgd 		}
    802  1.1  cgd 		if (c == 0)
    803  1.1  cgd 			tmpline[0] = '\0';
    804  1.1  cgd 	}
    805  1.1  cgd 	while ((c = getc(iop)) != EOF) {
    806  1.1  cgd 		c &= 0377;
    807  1.1  cgd 		if (c == IAC) {
    808  1.1  cgd 		    if ((c = getc(iop)) != EOF) {
    809  1.1  cgd 			c &= 0377;
    810  1.1  cgd 			switch (c) {
    811  1.1  cgd 			case WILL:
    812  1.1  cgd 			case WONT:
    813  1.1  cgd 				c = getc(iop);
    814  1.1  cgd 				printf("%c%c%c", IAC, DONT, 0377&c);
    815  1.1  cgd 				(void) fflush(stdout);
    816  1.1  cgd 				continue;
    817  1.1  cgd 			case DO:
    818  1.1  cgd 			case DONT:
    819  1.1  cgd 				c = getc(iop);
    820  1.1  cgd 				printf("%c%c%c", IAC, WONT, 0377&c);
    821  1.1  cgd 				(void) fflush(stdout);
    822  1.1  cgd 				continue;
    823  1.1  cgd 			case IAC:
    824  1.1  cgd 				break;
    825  1.1  cgd 			default:
    826  1.1  cgd 				continue;	/* ignore command */
    827  1.1  cgd 			}
    828  1.1  cgd 		    }
    829  1.1  cgd 		}
    830  1.1  cgd 		*cs++ = c;
    831  1.1  cgd 		if (--n <= 0 || c == '\n')
    832  1.1  cgd 			break;
    833  1.1  cgd 	}
    834  1.1  cgd 	if (c == EOF && cs == s)
    835  1.1  cgd 		return (NULL);
    836  1.1  cgd 	*cs++ = '\0';
    837  1.1  cgd 	if (debug)
    838  1.1  cgd 		syslog(LOG_DEBUG, "command: %s", s);
    839  1.1  cgd 	return (s);
    840  1.1  cgd }
    841  1.1  cgd 
    842  1.1  cgd static void
    843  1.1  cgd toolong()
    844  1.1  cgd {
    845  1.1  cgd 	time_t now;
    846  1.1  cgd 
    847  1.1  cgd 	reply(421,
    848  1.1  cgd 	  "Timeout (%d seconds): closing control connection.", timeout);
    849  1.1  cgd 	(void) time(&now);
    850  1.1  cgd 	if (logging) {
    851  1.1  cgd 		syslog(LOG_INFO,
    852  1.1  cgd 			"User %s timed out after %d seconds at %s",
    853  1.1  cgd 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
    854  1.1  cgd 	}
    855  1.1  cgd 	dologout(1);
    856  1.1  cgd }
    857  1.1  cgd 
    858  1.1  cgd yylex()
    859  1.1  cgd {
    860  1.1  cgd 	static int cpos, state;
    861  1.1  cgd 	register char *cp, *cp2;
    862  1.1  cgd 	register struct tab *p;
    863  1.1  cgd 	int n;
    864  1.1  cgd 	char c, *copy();
    865  1.1  cgd 
    866  1.1  cgd 	for (;;) {
    867  1.1  cgd 		switch (state) {
    868  1.1  cgd 
    869  1.1  cgd 		case CMD:
    870  1.1  cgd 			(void) signal(SIGALRM, toolong);
    871  1.1  cgd 			(void) alarm((unsigned) timeout);
    872  1.1  cgd 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
    873  1.1  cgd 				reply(221, "You could at least say goodbye.");
    874  1.1  cgd 				dologout(0);
    875  1.1  cgd 			}
    876  1.1  cgd 			(void) alarm(0);
    877  1.1  cgd #ifdef SETPROCTITLE
    878  1.1  cgd 			if (strncasecmp(cbuf, "PASS", 4) != NULL)
    879  1.1  cgd 				setproctitle("%s: %s", proctitle, cbuf);
    880  1.1  cgd #endif /* SETPROCTITLE */
    881  1.1  cgd 			if ((cp = index(cbuf, '\r'))) {
    882  1.1  cgd 				*cp++ = '\n';
    883  1.1  cgd 				*cp = '\0';
    884  1.1  cgd 			}
    885  1.1  cgd 			if ((cp = strpbrk(cbuf, " \n")))
    886  1.1  cgd 				cpos = cp - cbuf;
    887  1.1  cgd 			if (cpos == 0)
    888  1.1  cgd 				cpos = 4;
    889  1.1  cgd 			c = cbuf[cpos];
    890  1.1  cgd 			cbuf[cpos] = '\0';
    891  1.1  cgd 			upper(cbuf);
    892  1.1  cgd 			p = lookup(cmdtab, cbuf);
    893  1.1  cgd 			cbuf[cpos] = c;
    894  1.1  cgd 			if (p != 0) {
    895  1.1  cgd 				if (p->implemented == 0) {
    896  1.1  cgd 					nack(p->name);
    897  1.1  cgd 					longjmp(errcatch,0);
    898  1.1  cgd 					/* NOTREACHED */
    899  1.1  cgd 				}
    900  1.1  cgd 				state = p->state;
    901  1.1  cgd 				*(char **)&yylval = p->name;
    902  1.1  cgd 				return (p->token);
    903  1.1  cgd 			}
    904  1.1  cgd 			break;
    905  1.1  cgd 
    906  1.1  cgd 		case SITECMD:
    907  1.1  cgd 			if (cbuf[cpos] == ' ') {
    908  1.1  cgd 				cpos++;
    909  1.1  cgd 				return (SP);
    910  1.1  cgd 			}
    911  1.1  cgd 			cp = &cbuf[cpos];
    912  1.1  cgd 			if ((cp2 = strpbrk(cp, " \n")))
    913  1.1  cgd 				cpos = cp2 - cbuf;
    914  1.1  cgd 			c = cbuf[cpos];
    915  1.1  cgd 			cbuf[cpos] = '\0';
    916  1.1  cgd 			upper(cp);
    917  1.1  cgd 			p = lookup(sitetab, cp);
    918  1.1  cgd 			cbuf[cpos] = c;
    919  1.1  cgd 			if (p != 0) {
    920  1.1  cgd 				if (p->implemented == 0) {
    921  1.1  cgd 					state = CMD;
    922  1.1  cgd 					nack(p->name);
    923  1.1  cgd 					longjmp(errcatch,0);
    924  1.1  cgd 					/* NOTREACHED */
    925  1.1  cgd 				}
    926  1.1  cgd 				state = p->state;
    927  1.1  cgd 				*(char **)&yylval = p->name;
    928  1.1  cgd 				return (p->token);
    929  1.1  cgd 			}
    930  1.1  cgd 			state = CMD;
    931  1.1  cgd 			break;
    932  1.1  cgd 
    933  1.1  cgd 		case OSTR:
    934  1.1  cgd 			if (cbuf[cpos] == '\n') {
    935  1.1  cgd 				state = CMD;
    936  1.1  cgd 				return (CRLF);
    937  1.1  cgd 			}
    938  1.1  cgd 			/* FALLTHROUGH */
    939  1.1  cgd 
    940  1.1  cgd 		case STR1:
    941  1.1  cgd 		case ZSTR1:
    942  1.1  cgd 		dostr1:
    943  1.1  cgd 			if (cbuf[cpos] == ' ') {
    944  1.1  cgd 				cpos++;
    945  1.1  cgd 				state = state == OSTR ? STR2 : ++state;
    946  1.1  cgd 				return (SP);
    947  1.1  cgd 			}
    948  1.1  cgd 			break;
    949  1.1  cgd 
    950  1.1  cgd 		case ZSTR2:
    951  1.1  cgd 			if (cbuf[cpos] == '\n') {
    952  1.1  cgd 				state = CMD;
    953  1.1  cgd 				return (CRLF);
    954  1.1  cgd 			}
    955  1.1  cgd 			/* FALLTHROUGH */
    956  1.1  cgd 
    957  1.1  cgd 		case STR2:
    958  1.1  cgd 			cp = &cbuf[cpos];
    959  1.1  cgd 			n = strlen(cp);
    960  1.1  cgd 			cpos += n - 1;
    961  1.1  cgd 			/*
    962  1.1  cgd 			 * Make sure the string is nonempty and \n terminated.
    963  1.1  cgd 			 */
    964  1.1  cgd 			if (n > 1 && cbuf[cpos] == '\n') {
    965  1.1  cgd 				cbuf[cpos] = '\0';
    966  1.1  cgd 				*(char **)&yylval = copy(cp);
    967  1.1  cgd 				cbuf[cpos] = '\n';
    968  1.1  cgd 				state = ARGS;
    969  1.1  cgd 				return (STRING);
    970  1.1  cgd 			}
    971  1.1  cgd 			break;
    972  1.1  cgd 
    973  1.1  cgd 		case NSTR:
    974  1.1  cgd 			if (cbuf[cpos] == ' ') {
    975  1.1  cgd 				cpos++;
    976  1.1  cgd 				return (SP);
    977  1.1  cgd 			}
    978  1.1  cgd 			if (isdigit(cbuf[cpos])) {
    979  1.1  cgd 				cp = &cbuf[cpos];
    980  1.1  cgd 				while (isdigit(cbuf[++cpos]))
    981  1.1  cgd 					;
    982  1.1  cgd 				c = cbuf[cpos];
    983  1.1  cgd 				cbuf[cpos] = '\0';
    984  1.1  cgd 				yylval = atoi(cp);
    985  1.1  cgd 				cbuf[cpos] = c;
    986  1.1  cgd 				state = STR1;
    987  1.1  cgd 				return (NUMBER);
    988  1.1  cgd 			}
    989  1.1  cgd 			state = STR1;
    990  1.1  cgd 			goto dostr1;
    991  1.1  cgd 
    992  1.1  cgd 		case ARGS:
    993  1.1  cgd 			if (isdigit(cbuf[cpos])) {
    994  1.1  cgd 				cp = &cbuf[cpos];
    995  1.1  cgd 				while (isdigit(cbuf[++cpos]))
    996  1.1  cgd 					;
    997  1.1  cgd 				c = cbuf[cpos];
    998  1.1  cgd 				cbuf[cpos] = '\0';
    999  1.1  cgd 				yylval = atoi(cp);
   1000  1.1  cgd 				cbuf[cpos] = c;
   1001  1.1  cgd 				return (NUMBER);
   1002  1.1  cgd 			}
   1003  1.1  cgd 			switch (cbuf[cpos++]) {
   1004  1.1  cgd 
   1005  1.1  cgd 			case '\n':
   1006  1.1  cgd 				state = CMD;
   1007  1.1  cgd 				return (CRLF);
   1008  1.1  cgd 
   1009  1.1  cgd 			case ' ':
   1010  1.1  cgd 				return (SP);
   1011  1.1  cgd 
   1012  1.1  cgd 			case ',':
   1013  1.1  cgd 				return (COMMA);
   1014  1.1  cgd 
   1015  1.1  cgd 			case 'A':
   1016  1.1  cgd 			case 'a':
   1017  1.1  cgd 				return (A);
   1018  1.1  cgd 
   1019  1.1  cgd 			case 'B':
   1020  1.1  cgd 			case 'b':
   1021  1.1  cgd 				return (B);
   1022  1.1  cgd 
   1023  1.1  cgd 			case 'C':
   1024  1.1  cgd 			case 'c':
   1025  1.1  cgd 				return (C);
   1026  1.1  cgd 
   1027  1.1  cgd 			case 'E':
   1028  1.1  cgd 			case 'e':
   1029  1.1  cgd 				return (E);
   1030  1.1  cgd 
   1031  1.1  cgd 			case 'F':
   1032  1.1  cgd 			case 'f':
   1033  1.1  cgd 				return (F);
   1034  1.1  cgd 
   1035  1.1  cgd 			case 'I':
   1036  1.1  cgd 			case 'i':
   1037  1.1  cgd 				return (I);
   1038  1.1  cgd 
   1039  1.1  cgd 			case 'L':
   1040  1.1  cgd 			case 'l':
   1041  1.1  cgd 				return (L);
   1042  1.1  cgd 
   1043  1.1  cgd 			case 'N':
   1044  1.1  cgd 			case 'n':
   1045  1.1  cgd 				return (N);
   1046  1.1  cgd 
   1047  1.1  cgd 			case 'P':
   1048  1.1  cgd 			case 'p':
   1049  1.1  cgd 				return (P);
   1050  1.1  cgd 
   1051  1.1  cgd 			case 'R':
   1052  1.1  cgd 			case 'r':
   1053  1.1  cgd 				return (R);
   1054  1.1  cgd 
   1055  1.1  cgd 			case 'S':
   1056  1.1  cgd 			case 's':
   1057  1.1  cgd 				return (S);
   1058  1.1  cgd 
   1059  1.1  cgd 			case 'T':
   1060  1.1  cgd 			case 't':
   1061  1.1  cgd 				return (T);
   1062  1.1  cgd 
   1063  1.1  cgd 			}
   1064  1.1  cgd 			break;
   1065  1.1  cgd 
   1066  1.1  cgd 		default:
   1067  1.1  cgd 			fatal("Unknown state in scanner.");
   1068  1.1  cgd 		}
   1069  1.1  cgd 		yyerror((char *) 0);
   1070  1.1  cgd 		state = CMD;
   1071  1.1  cgd 		longjmp(errcatch,0);
   1072  1.1  cgd 	}
   1073  1.1  cgd }
   1074  1.1  cgd 
   1075  1.1  cgd upper(s)
   1076  1.1  cgd 	register char *s;
   1077  1.1  cgd {
   1078  1.1  cgd 	while (*s != '\0') {
   1079  1.1  cgd 		if (islower(*s))
   1080  1.1  cgd 			*s = toupper(*s);
   1081  1.1  cgd 		s++;
   1082  1.1  cgd 	}
   1083  1.1  cgd }
   1084  1.1  cgd 
   1085  1.1  cgd char *
   1086  1.1  cgd copy(s)
   1087  1.1  cgd 	char *s;
   1088  1.1  cgd {
   1089  1.1  cgd 	char *p;
   1090  1.1  cgd 
   1091  1.1  cgd 	p = malloc((unsigned) strlen(s) + 1);
   1092  1.1  cgd 	if (p == NULL)
   1093  1.1  cgd 		fatal("Ran out of memory.");
   1094  1.1  cgd 	(void) strcpy(p, s);
   1095  1.1  cgd 	return (p);
   1096  1.1  cgd }
   1097  1.1  cgd 
   1098  1.1  cgd help(ctab, s)
   1099  1.1  cgd 	struct tab *ctab;
   1100  1.1  cgd 	char *s;
   1101  1.1  cgd {
   1102  1.1  cgd 	register struct tab *c;
   1103  1.1  cgd 	register int width, NCMDS;
   1104  1.1  cgd 	char *type;
   1105  1.1  cgd 
   1106  1.1  cgd 	if (ctab == sitetab)
   1107  1.1  cgd 		type = "SITE ";
   1108  1.1  cgd 	else
   1109  1.1  cgd 		type = "";
   1110  1.1  cgd 	width = 0, NCMDS = 0;
   1111  1.1  cgd 	for (c = ctab; c->name != NULL; c++) {
   1112  1.1  cgd 		int len = strlen(c->name);
   1113  1.1  cgd 
   1114  1.1  cgd 		if (len > width)
   1115  1.1  cgd 			width = len;
   1116  1.1  cgd 		NCMDS++;
   1117  1.1  cgd 	}
   1118  1.1  cgd 	width = (width + 8) &~ 7;
   1119  1.1  cgd 	if (s == 0) {
   1120  1.1  cgd 		register int i, j, w;
   1121  1.1  cgd 		int columns, lines;
   1122  1.1  cgd 
   1123  1.1  cgd 		lreply(214, "The following %scommands are recognized %s.",
   1124  1.1  cgd 		    type, "(* =>'s unimplemented)");
   1125  1.1  cgd 		columns = 76 / width;
   1126  1.1  cgd 		if (columns == 0)
   1127  1.1  cgd 			columns = 1;
   1128  1.1  cgd 		lines = (NCMDS + columns - 1) / columns;
   1129  1.1  cgd 		for (i = 0; i < lines; i++) {
   1130  1.1  cgd 			printf("   ");
   1131  1.1  cgd 			for (j = 0; j < columns; j++) {
   1132  1.1  cgd 				c = ctab + j * lines + i;
   1133  1.1  cgd 				printf("%s%c", c->name,
   1134  1.1  cgd 					c->implemented ? ' ' : '*');
   1135  1.1  cgd 				if (c + lines >= &ctab[NCMDS])
   1136  1.1  cgd 					break;
   1137  1.1  cgd 				w = strlen(c->name) + 1;
   1138  1.1  cgd 				while (w < width) {
   1139  1.1  cgd 					putchar(' ');
   1140  1.1  cgd 					w++;
   1141  1.1  cgd 				}
   1142  1.1  cgd 			}
   1143  1.1  cgd 			printf("\r\n");
   1144  1.1  cgd 		}
   1145  1.1  cgd 		(void) fflush(stdout);
   1146  1.1  cgd 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
   1147  1.1  cgd 		return;
   1148  1.1  cgd 	}
   1149  1.1  cgd 	upper(s);
   1150  1.1  cgd 	c = lookup(ctab, s);
   1151  1.1  cgd 	if (c == (struct tab *)0) {
   1152  1.1  cgd 		reply(502, "Unknown command %s.", s);
   1153  1.1  cgd 		return;
   1154  1.1  cgd 	}
   1155  1.1  cgd 	if (c->implemented)
   1156  1.1  cgd 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
   1157  1.1  cgd 	else
   1158  1.1  cgd 		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
   1159  1.1  cgd 		    c->name, c->help);
   1160  1.1  cgd }
   1161  1.1  cgd 
   1162  1.1  cgd sizecmd(filename)
   1163  1.1  cgd char *filename;
   1164  1.1  cgd {
   1165  1.1  cgd 	switch (type) {
   1166  1.1  cgd 	case TYPE_L:
   1167  1.1  cgd 	case TYPE_I: {
   1168  1.1  cgd 		struct stat stbuf;
   1169  1.1  cgd 		if (stat(filename, &stbuf) < 0 ||
   1170  1.1  cgd 		    (stbuf.st_mode&S_IFMT) != S_IFREG)
   1171  1.1  cgd 			reply(550, "%s: not a plain file.", filename);
   1172  1.1  cgd 		else
   1173  1.1  cgd 			reply(213, "%lu", stbuf.st_size);
   1174  1.1  cgd 		break;}
   1175  1.1  cgd 	case TYPE_A: {
   1176  1.1  cgd 		FILE *fin;
   1177  1.1  cgd 		register int c;
   1178  1.1  cgd 		register long count;
   1179  1.1  cgd 		struct stat stbuf;
   1180  1.1  cgd 		fin = fopen(filename, "r");
   1181  1.1  cgd 		if (fin == NULL) {
   1182  1.1  cgd 			perror_reply(550, filename);
   1183  1.1  cgd 			return;
   1184  1.1  cgd 		}
   1185  1.1  cgd 		if (fstat(fileno(fin), &stbuf) < 0 ||
   1186  1.1  cgd 		    (stbuf.st_mode&S_IFMT) != S_IFREG) {
   1187  1.1  cgd 			reply(550, "%s: not a plain file.", filename);
   1188  1.1  cgd 			(void) fclose(fin);
   1189  1.1  cgd 			return;
   1190  1.1  cgd 		}
   1191  1.1  cgd 
   1192  1.1  cgd 		count = 0;
   1193  1.1  cgd 		while((c=getc(fin)) != EOF) {
   1194  1.1  cgd 			if (c == '\n')	/* will get expanded to \r\n */
   1195  1.1  cgd 				count++;
   1196  1.1  cgd 			count++;
   1197  1.1  cgd 		}
   1198  1.1  cgd 		(void) fclose(fin);
   1199  1.1  cgd 
   1200  1.1  cgd 		reply(213, "%ld", count);
   1201  1.1  cgd 		break;}
   1202  1.1  cgd 	default:
   1203  1.1  cgd 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
   1204  1.1  cgd 	}
   1205  1.1  cgd }
   1206