ftpcmd.y revision 59121
122347Spst/* ftpcmd.y: yacc parser for the FTP daemon.
222347Spst
329964Sache%%% portions-copyright-cmetz-96
459121SkrisPortions of this software are Copyright 1996-1998 by Craig Metz, All Rights
522347SpstReserved. The Inner Net License Version 2 applies to these portions of
622347Spstthe software.
722347SpstYou should have received a copy of the license with this software. If
822347Spstyou didn't get a copy, you may request one from <license@inner.net>.
922347Spst
1022347Spst	History:
1122347Spst
1222347Spst	Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here.
1322347Spst        Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings.
1422347Spst                Use FUNCTION declaration et al. Removed useless strings.
1522347Spst                Changed some char []s to char *s. Deleted comment address.
1622347Spst                Changed tmpline references to be more pure-pointer
1722347Spst                references. Changed tmpline declaration back to char [].
1822347Spst	Modified at NRL for OPIE 2.1. Minor changes for autoconf.
1922347Spst        Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[]
2022347Spst                -- fixes problems experienced by bison users. Merged in new
2122347Spst                PORT attack fixes from Hobbit.
2222347Spst	Modified at NRL for OPIE 2.0.
2322347Spst	Originally from BSD.
2459121Skris
2559121Skris$FreeBSD: head/contrib/opie/ftpcmd.y 59121 2000-04-10 11:18:54Z kris $
2622347Spst*/
2722347Spst/*
2822347Spst * Copyright (c) 1985, 1988 Regents of the University of California.
2922347Spst * All rights reserved.
3022347Spst *
3122347Spst * Redistribution and use in source and binary forms, with or without
3222347Spst * modification, are permitted provided that the following conditions
3322347Spst * are met:
3422347Spst * 1. Redistributions of source code must retain the above copyright
3522347Spst *    notice, this list of conditions and the following disclaimer.
3622347Spst * 2. Redistributions in binary form must reproduce the above copyright
3722347Spst *    notice, this list of conditions and the following disclaimer in the
3822347Spst *    documentation and/or other materials provided with the distribution.
3922347Spst * 3. All advertising materials mentioning features or use of this software
4022347Spst *    must display the following acknowledgement:
4122347Spst *	This product includes software developed by the University of
4222347Spst *	California, Berkeley and its contributors.
4322347Spst * 4. Neither the name of the University nor the names of its contributors
4422347Spst *    may be used to endorse or promote products derived from this software
4522347Spst *    without specific prior written permission.
4622347Spst *
4722347Spst * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
4822347Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4922347Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5022347Spst * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5122347Spst * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5222347Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5322347Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5422347Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5522347Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5622347Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5722347Spst * SUCH DAMAGE.
5822347Spst *
5922347Spst *	@(#)ftpcmd.y	5.24 (Berkeley) 2/25/91
6022347Spst */
6122347Spst
6222347Spst/*
6322347Spst * Grammar for FTP commands.
6422347Spst * See RFC 959.
6522347Spst */
6622347Spst
6722347Spst%{
6822347Spst#include "opie_cfg.h"
6922347Spst
7022347Spst#include <sys/param.h>
7122347Spst#include <sys/types.h>
7222347Spst#include <sys/socket.h>
7322347Spst#include <sys/stat.h>
7422347Spst#include <netinet/in.h>
7522347Spst#include <arpa/ftp.h>
7622347Spst#include <signal.h>
7722347Spst#include <setjmp.h>
7822347Spst#include <syslog.h>
7922347Spst#if TM_IN_SYS_TIME
8022347Spst#include <sys/time.h>
8122347Spst#else /* TM_IN_SYS_TIME */
8222347Spst#include <time.h>
8322347Spst#endif /* TM_IN_SYS_TIME */
8422347Spst#include <pwd.h>
8522347Spst#include <unistd.h>
8622347Spst#include <stdio.h>
8722347Spst#include <ctype.h>
8822347Spst#include <stdlib.h>
8922347Spst#include <string.h>
9022347Spst
9122347Spst#include "opie.h"
9222347Spst
9322347Spst#if HAVE_LS_G_FLAG
9422347Spst#define LS_COMMAND "/bin/ls -lgA"
9522347Spst#else /* HAVE_LS_G_FLAG */
9622347Spst#define LS_COMMAND "/bin/ls -lA"
9722347Spst#endif /* HAVE_LS_G_FLAG */
9822347Spst
9922347Spstextern	struct sockaddr_in data_dest;
10022347Spstextern  struct sockaddr_in his_addr;
10122347Spstextern	int logged_in;
10222347Spstextern	struct passwd *pw;
10322347Spstextern	int guest;
10422347Spstextern	int type;
10522347Spstextern	int form;
10622347Spstextern	int debug;
10722347Spstextern	int timeout;
10822347Spstextern	int maxtimeout;
10922347Spstextern  int pdata;
11022347Spstextern	char *remotehost;
11122347Spstextern	char *proctitle;
11222347Spstextern	char *globerr;
11322347Spstextern	int usedefault;
11422347Spstextern  int transflag;
11522347Spstextern  char tmpline[];
11622347Spstchar	**ftpglob();
11722347Spst
11822347SpstVOIDRET dologout __P((int));
11922347SpstVOIDRET upper __P((char *));
12022347SpstVOIDRET nack __P((char *));
12122347SpstVOIDRET opiefatal __P((char *));
12222347Spst
12322347SpstVOIDRET pass __P((char *));
12422347Spstint user __P((char *));
12522347SpstVOIDRET passive __P((void));
12622347SpstVOIDRET retrieve __P((char *, char *));
12722347SpstVOIDRET store __P((char *, char *, int));
12822347SpstVOIDRET send_file_list __P((char *));
12922347SpstVOIDRET statfilecmd __P((char *));
13022347SpstVOIDRET statcmd __P((void));
13122347SpstVOIDRET delete __P((char *));
13222347SpstVOIDRET renamecmd __P((char *, char *));
13322347SpstVOIDRET cwd __P((char *));
13422347SpstVOIDRET makedir __P((char *));
13522347SpstVOIDRET removedir __P((char *));
13622347SpstVOIDRET pwd __P((void));
13722347Spst
13822347SpstVOIDRET sizecmd __P((char *));
13922347Spst
14022347Spstoff_t	restart_point;
14122347Spst
14222347Spststatic	int cmd_type;
14322347Spststatic	int cmd_form;
14422347Spststatic	int cmd_bytesz;
14522347Spststatic  unsigned short cliport = 0;
14622347Spstchar	cbuf[512];
14722347Spstchar	*fromname;
14822347Spst
14922347Spststruct tab {
15022347Spst	char	*name;
15122347Spst	short	token;
15222347Spst	short	state;
15322347Spst	short	implemented;	/* 1 if command is implemented */
15422347Spst	char	*help;
15522347Spst};
15622347Spst
15722347SpstVOIDRET help __P((struct tab *, char *));
15822347Spst
15922347Spststruct tab cmdtab[], sitetab[];
16022347Spst
16122347Spst%}
16222347Spst
16322347Spst%token
16422347Spst	A	B	C	E	F	I
16522347Spst	L	N	P	R	S	T
16622347Spst
16722347Spst	SP	CRLF	COMMA	STRING	NUMBER
16822347Spst
16922347Spst	USER	PASS	ACCT	REIN	QUIT	PORT
17022347Spst	PASV	TYPE	STRU	MODE	RETR	STOR
17122347Spst	APPE	MLFL	MAIL	MSND	MSOM	MSAM
17222347Spst	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
17322347Spst	ABOR	DELE	CWD	LIST	NLST	SITE
17422347Spst	STAT	HELP	NOOP	MKD	RMD	PWD
17522347Spst	CDUP	STOU	SMNT	SYST	SIZE	MDTM
17622347Spst
17722347Spst	UMASK	IDLE	CHMOD
17822347Spst
17922347Spst	LEXERR
18022347Spst
18122347Spst%start	cmd_list
18222347Spst
18322347Spst%%
18422347Spst
18522347Spstcmd_list:	/* empty */
18622347Spst	|	cmd_list cmd
18722347Spst		= {
18822347Spst			fromname = (char *) 0;
18922347Spst			restart_point = (off_t) 0;
19022347Spst		}
19122347Spst	|	cmd_list rcmd
19222347Spst	;
19322347Spst
19422347Spstcmd:		USER SP username CRLF
19522347Spst		= {
19622347Spst			user((char *) $3);
19722347Spst			free((char *) $3);
19822347Spst		}
19922347Spst	|	PASS SP password CRLF
20022347Spst		= {
20122347Spst			pass((char *) $3);
20222347Spst			free((char *) $3);
20322347Spst		}
20422347Spst        |   PORT check_login SP host_port CRLF
20522347Spst                = {
20622347Spst             usedefault = 0;
20722347Spst             if (pdata >= 0) {
20822347Spst                 (void) close(pdata);
20922347Spst                 pdata = -1;
21022347Spst             }
21122347Spst/* H* port fix, part B: admonish the twit.
21222347Spst   Also require login before PORT works */
21322347Spst            if ($2) {
21422347Spst              if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) {
21522347Spst                reply(200, "PORT command successful.");
21622347Spst              } else {
21722347Spst                syslog (LOG_WARNING, "refused %s from %s",
21822347Spst                       cbuf, remotehost);
21922347Spst                reply(500, "You've GOT to be joking.");
22022347Spst              }
22122347Spst            }
22222347Spst                }
22322347Spst/*	|	PASV CRLF
22422347Spst		= {
22522347Spst			passive();
22622347Spst		} */
22722347Spst    |   PASV check_login CRLF
22822347Spst        = {
22922347Spst/* Require login for PASV, too.  This actually fixes a bug -- telnet to an
23022347Spst   unfixed wu-ftpd and type PASV first off, and it crashes! */
23122347Spst            if ($2) {
23222347Spst                passive();
23322347Spst            }
23422347Spst        }
23522347Spst	|	TYPE SP type_code CRLF
23622347Spst		= {
23722347Spst			switch (cmd_type) {
23822347Spst
23922347Spst			case TYPE_A:
24022347Spst				if (cmd_form == FORM_N) {
24122347Spst					reply(200, "Type set to A.");
24222347Spst					type = cmd_type;
24322347Spst					form = cmd_form;
24422347Spst				} else
24522347Spst					reply(504, "Form must be N.");
24622347Spst				break;
24722347Spst
24822347Spst			case TYPE_E:
24922347Spst				reply(504, "Type E not implemented.");
25022347Spst				break;
25122347Spst
25222347Spst			case TYPE_I:
25322347Spst				reply(200, "Type set to I.");
25422347Spst				type = cmd_type;
25522347Spst				break;
25622347Spst
25722347Spst			case TYPE_L:
25822347Spst#if NBBY == 8
25922347Spst				if (cmd_bytesz == 8) {
26022347Spst					reply(200,
26122347Spst					    "Type set to L (byte size 8).");
26222347Spst					type = cmd_type;
26322347Spst				} else
26422347Spst					reply(504, "Byte size must be 8.");
26522347Spst#else /* NBBY == 8 */
26622347Spst				UNIMPLEMENTED for NBBY != 8
26722347Spst#endif /* NBBY == 8 */
26822347Spst			}
26922347Spst		}
27022347Spst	|	STRU SP struct_code CRLF
27122347Spst		= {
27222347Spst			switch ($3) {
27322347Spst
27422347Spst			case STRU_F:
27522347Spst				reply(200, "STRU F ok.");
27622347Spst				break;
27722347Spst
27822347Spst			default:
27922347Spst				reply(504, "Unimplemented STRU type.");
28022347Spst			}
28122347Spst		}
28222347Spst	|	MODE SP mode_code CRLF
28322347Spst		= {
28422347Spst			switch ($3) {
28522347Spst
28622347Spst			case MODE_S:
28722347Spst				reply(200, "MODE S ok.");
28822347Spst				break;
28922347Spst
29022347Spst			default:
29122347Spst				reply(502, "Unimplemented MODE type.");
29222347Spst			}
29322347Spst		}
29422347Spst	|	ALLO SP NUMBER CRLF
29522347Spst		= {
29622347Spst			reply(202, "ALLO command ignored.");
29722347Spst		}
29822347Spst	|	ALLO SP NUMBER SP R SP NUMBER CRLF
29922347Spst		= {
30022347Spst			reply(202, "ALLO command ignored.");
30122347Spst		}
30222347Spst	|	RETR check_login SP pathname CRLF
30322347Spst		= {
30422347Spst			if ($2 && $4)
30522347Spst				retrieve((char *) 0, (char *) $4);
30622347Spst			if ($4)
30722347Spst				free((char *) $4);
30822347Spst		}
30922347Spst	|	STOR check_login SP pathname CRLF
31022347Spst		= {
31122347Spst			if ($2 && $4)
31222347Spst				store((char *) $4, "w", 0);
31322347Spst			if ($4)
31422347Spst				free((char *) $4);
31522347Spst		}
31622347Spst	|	APPE check_login SP pathname CRLF
31722347Spst		= {
31822347Spst			if ($2 && $4)
31922347Spst				store((char *) $4, "a", 0);
32022347Spst			if ($4)
32122347Spst				free((char *) $4);
32222347Spst		}
32322347Spst	|	NLST check_login CRLF
32422347Spst		= {
32522347Spst			if ($2)
32622347Spst				send_file_list(".");
32722347Spst		}
32822347Spst	|	NLST check_login SP STRING CRLF
32922347Spst		= {
33022347Spst			if ($2 && $4)
33122347Spst				send_file_list((char *) $4);
33222347Spst			if ($4)
33322347Spst				free((char *) $4);
33422347Spst		}
33522347Spst	|	LIST check_login CRLF
33622347Spst		= {
33722347Spst			if ($2)
33822347Spst				retrieve(LS_COMMAND, "");
33922347Spst		}
34022347Spst	|	LIST check_login SP pathname CRLF
34122347Spst		= {
34222347Spst			if ($2 && $4)
34322347Spst                                {
34422347Spst                                char buffer[sizeof(LS_COMMAND)+3];
34522347Spst                                strcpy(buffer, LS_COMMAND);
34622347Spst                                strcat(buffer, " %s");
34722347Spst				retrieve(buffer, (char *) $4);
34822347Spst                                }
34922347Spst			if ($4)
35022347Spst				free((char *) $4);
35122347Spst		}
35222347Spst	|	STAT check_login SP pathname CRLF
35322347Spst		= {
35422347Spst			if ($2 && $4)
35522347Spst				statfilecmd((char *) $4);
35622347Spst			if ($4)
35722347Spst				free((char *) $4);
35822347Spst		}
35922347Spst	|	STAT CRLF
36022347Spst		= {
36122347Spst			statcmd();
36222347Spst		}
36322347Spst	|	DELE check_login SP pathname CRLF
36422347Spst		= {
36522347Spst			if ($2 && $4)
36622347Spst				delete((char *) $4);
36722347Spst			if ($4)
36822347Spst				free((char *) $4);
36922347Spst		}
37022347Spst	|	RNTO SP pathname CRLF
37122347Spst		= {
37222347Spst			if (fromname) {
37322347Spst				renamecmd(fromname, (char *) $3);
37422347Spst				free(fromname);
37522347Spst				fromname = (char *) 0;
37622347Spst			} else {
37722347Spst				reply(503, "Bad sequence of commands.");
37822347Spst			}
37922347Spst			free((char *) $3);
38022347Spst		}
38122347Spst	|	ABOR CRLF
38222347Spst		= {
38322347Spst			reply(225, "ABOR command successful.");
38422347Spst		}
38522347Spst	|	CWD check_login CRLF
38622347Spst		= {
38722347Spst			if ($2)
38822347Spst				cwd(pw->pw_dir);
38922347Spst		}
39022347Spst	|	CWD check_login SP pathname CRLF
39122347Spst		= {
39222347Spst			if ($2 && $4)
39322347Spst				cwd((char *) $4);
39422347Spst			if ($4)
39522347Spst				free((char *) $4);
39622347Spst		}
39722347Spst	|	HELP CRLF
39822347Spst		= {
39922347Spst			help(cmdtab, (char *) 0);
40022347Spst		}
40122347Spst	|	HELP SP STRING CRLF
40222347Spst		= {
40322347Spst			register char *cp = (char *)$3;
40422347Spst
40522347Spst			if (strncasecmp(cp, "SITE", 4) == 0) {
40622347Spst				cp = (char *)$3 + 4;
40722347Spst				if (*cp == ' ')
40822347Spst					cp++;
40922347Spst				if (*cp)
41022347Spst					help(sitetab, cp);
41122347Spst				else
41222347Spst					help(sitetab, (char *) 0);
41322347Spst			} else
41422347Spst				help(cmdtab, (char *) $3);
41522347Spst		}
41622347Spst	|	NOOP CRLF
41722347Spst		= {
41822347Spst			reply(200, "NOOP command successful.");
41922347Spst		}
42022347Spst	|	MKD check_login SP pathname CRLF
42122347Spst		= {
42222347Spst			if ($2 && $4)
42322347Spst				makedir((char *) $4);
42422347Spst			if ($4)
42522347Spst				free((char *) $4);
42622347Spst		}
42722347Spst	|	RMD check_login SP pathname CRLF
42822347Spst		= {
42922347Spst			if ($2 && $4)
43022347Spst				removedir((char *) $4);
43122347Spst			if ($4)
43222347Spst				free((char *) $4);
43322347Spst		}
43422347Spst	|	PWD check_login CRLF
43522347Spst		= {
43622347Spst			if ($2)
43722347Spst				pwd();
43822347Spst		}
43922347Spst	|	CDUP check_login CRLF
44022347Spst		= {
44122347Spst			if ($2)
44222347Spst				cwd("..");
44322347Spst		}
44422347Spst	|	SITE SP HELP CRLF
44522347Spst		= {
44622347Spst			help(sitetab, (char *) 0);
44722347Spst		}
44822347Spst	|	SITE SP HELP SP STRING CRLF
44922347Spst		= {
45022347Spst			help(sitetab, (char *) $5);
45122347Spst		}
45222347Spst	|	SITE SP UMASK check_login CRLF
45322347Spst		= {
45422347Spst			int oldmask;
45522347Spst
45622347Spst			if ($4) {
45722347Spst				oldmask = umask(0);
45822347Spst				(void) umask(oldmask);
45922347Spst				reply(200, "Current UMASK is %03o", oldmask);
46022347Spst			}
46122347Spst		}
46222347Spst	|	SITE SP UMASK check_login SP octal_number CRLF
46322347Spst		= {
46422347Spst			int oldmask;
46522347Spst
46622347Spst			if ($4) {
46722347Spst				if (($6 == -1) || ($6 > 0777)) {
46822347Spst					reply(501, "Bad UMASK value");
46922347Spst				} else {
47022347Spst					oldmask = umask($6);
47122347Spst					reply(200,
47222347Spst					    "UMASK set to %03o (was %03o)",
47322347Spst					    $6, oldmask);
47422347Spst				}
47522347Spst			}
47622347Spst		}
47722347Spst	|	SITE SP CHMOD check_login SP octal_number SP pathname CRLF
47822347Spst		= {
47922347Spst			if ($4 && $8) {
48022347Spst				if ($6 > 0777)
48122347Spst					reply(501,
48222347Spst				"CHMOD: Mode value must be between 0 and 0777");
48322347Spst				else if (chmod((char *) $8, $6) < 0)
48422347Spst					perror_reply(550, (char *) $8);
48522347Spst				else
48622347Spst					reply(200, "CHMOD command successful.");
48722347Spst			}
48822347Spst			if ($8)
48922347Spst				free((char *) $8);
49022347Spst		}
49122347Spst	|	SITE SP IDLE CRLF
49222347Spst		= {
49322347Spst			reply(200,
49422347Spst			    "Current IDLE time limit is %d seconds; max %d",
49522347Spst				timeout, maxtimeout);
49622347Spst		}
49722347Spst	|	SITE SP IDLE SP NUMBER CRLF
49822347Spst		= {
49922347Spst			if ($5 < 30 || $5 > maxtimeout) {
50022347Spst				reply(501,
50122347Spst			"Maximum IDLE time must be between 30 and %d seconds",
50222347Spst				    maxtimeout);
50322347Spst			} else {
50422347Spst				timeout = $5;
50522347Spst				(void) alarm((unsigned) timeout);
50622347Spst				reply(200,
50722347Spst				    "Maximum IDLE time set to %d seconds",
50822347Spst				    timeout);
50922347Spst			}
51022347Spst		}
51122347Spst	|	STOU check_login SP pathname CRLF
51222347Spst		= {
51322347Spst			if ($2 && $4)
51422347Spst				store((char *) $4, "w", 1);
51522347Spst			if ($4)
51622347Spst				free((char *) $4);
51722347Spst		}
51822347Spst	|	SYST CRLF
51922347Spst		= {
52022347Spst#ifdef unix
52122347Spst#ifdef BSD
52222347Spst			reply(215, "UNIX Type: L%d Version: BSD-%d",
52322347Spst				NBBY, BSD);
52422347Spst#else /* BSD */
52522347Spst			reply(215, "UNIX Type: L%d", NBBY);
52622347Spst#endif /* BSD */
52722347Spst#else /* unix */
52822347Spst			reply(215, "UNKNOWN Type: L%d", NBBY);
52922347Spst#endif /* unix */
53022347Spst		}
53122347Spst
53222347Spst		/*
53322347Spst		 * SIZE is not in RFC959, but Postel has blessed it and
53422347Spst		 * it will be in the updated RFC.
53522347Spst		 *
53622347Spst		 * Return size of file in a format suitable for
53722347Spst		 * using with RESTART (we just count bytes).
53822347Spst		 */
53922347Spst	|	SIZE check_login SP pathname CRLF
54022347Spst		= {
54122347Spst			if ($2 && $4)
54222347Spst				sizecmd((char *) $4);
54322347Spst			if ($4)
54422347Spst				free((char *) $4);
54522347Spst		}
54622347Spst
54722347Spst		/*
54822347Spst		 * MDTM is not in RFC959, but Postel has blessed it and
54922347Spst		 * it will be in the updated RFC.
55022347Spst		 *
55122347Spst		 * Return modification time of file as an ISO 3307
55222347Spst		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
55322347Spst		 * where xxx is the fractional second (of any precision,
55422347Spst		 * not necessarily 3 digits)
55522347Spst		 */
55622347Spst	|	MDTM check_login SP pathname CRLF
55722347Spst		= {
55822347Spst			if ($2 && $4) {
55922347Spst				struct stat stbuf;
56022347Spst				if (stat((char *) $4, &stbuf) < 0)
56122347Spst					perror_reply(550, (char *) $4);
56222347Spst				else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
56322347Spst					reply(550, "%s: not a plain file.",
56422347Spst						(char *) $4);
56522347Spst				} else {
56622347Spst					register struct tm *t;
56722347Spst					struct tm *gmtime();
56822347Spst					t = gmtime(&stbuf.st_mtime);
56922347Spst					reply(213,
57031940Salex					    "%d%02d%02d%02d%02d%02d",
57131940Salex					    t->tm_year+1900, t->tm_mon+1, t->tm_mday,
57222347Spst					    t->tm_hour, t->tm_min, t->tm_sec);
57322347Spst				}
57422347Spst			}
57522347Spst			if ($4)
57622347Spst				free((char *) $4);
57722347Spst		}
57822347Spst	|	QUIT CRLF
57922347Spst		= {
58022347Spst			reply(221, "Goodbye.");
58122347Spst			dologout(0);
58222347Spst		}
58322347Spst	|	error CRLF
58422347Spst		= {
58522347Spst			yyerrok;
58622347Spst		}
58722347Spst	;
58822347Spstrcmd:		RNFR check_login SP pathname CRLF
58922347Spst		= {
59022347Spst			char *renamefrom();
59122347Spst
59222347Spst			restart_point = (off_t) 0;
59322347Spst			if ($2 && $4) {
59422347Spst				fromname = renamefrom((char *) $4);
59522347Spst				if (fromname == (char *) 0 && $4) {
59622347Spst					free((char *) $4);
59722347Spst				}
59822347Spst			}
59922347Spst		}
60022347Spst	|	REST SP byte_size CRLF
60122347Spst		= {
60222347Spst			long atol();
60322347Spst
60422347Spst			fromname = (char *) 0;
60522347Spst			restart_point = $3;
60622347Spst			reply(350, "Restarting at %ld. %s", restart_point,
60722347Spst			    "Send STORE or RETRIEVE to initiate transfer.");
60822347Spst		}
60922347Spst	;
61022347Spst
61122347Spstusername:	STRING
61222347Spst	;
61322347Spst
61422347Spstpassword:	/* empty */
61522347Spst		= {
61622347Spst			*(char **)&($$) = (char *)calloc(1, sizeof(char));
61722347Spst		}
61822347Spst	|	STRING
61922347Spst	;
62022347Spst
62122347Spstbyte_size:	NUMBER
62222347Spst	;
62322347Spst
62422347Spsthost_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
62522347Spst		NUMBER COMMA NUMBER
62622347Spst		= {
62722347Spst			register char *a, *p;
62822347Spst
62922347Spst			a = (char *)&data_dest.sin_addr;
63022347Spst			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
63122347Spst
63222347Spst/* H* port fix, part A-1: Check the args against the client addr */
63322347Spst            p = (char *)&his_addr.sin_addr;
63422347Spst             if (memcmp (a, p, sizeof (data_dest.sin_addr)))
63522347Spst                 memset (a, 0, sizeof (data_dest.sin_addr));     /* XXX */
63622347Spst
63722347Spst			p = (char *)&data_dest.sin_port;
63822347Spst
63922347Spst/* H* port fix, part A-2: only allow client ports in "user space" */
64022347Spst            p[0] = 0; p[1] = 0;
64122347Spst            cliport = ($9 << 8) + $11;
64222347Spst            if (cliport > 1023) {
64322347Spst                 p[0] = $9; p[1] = $11;
64422347Spst            }
64522347Spst
64622347Spst			p[0] = $9; p[1] = $11;
64722347Spst			data_dest.sin_family = AF_INET;
64822347Spst		}
64922347Spst	;
65022347Spst
65122347Spstform_code:	N
65222347Spst	= {
65322347Spst		$$ = FORM_N;
65422347Spst	}
65522347Spst	|	T
65622347Spst	= {
65722347Spst		$$ = FORM_T;
65822347Spst	}
65922347Spst	|	C
66022347Spst	= {
66122347Spst		$$ = FORM_C;
66222347Spst	}
66322347Spst	;
66422347Spst
66522347Spsttype_code:	A
66622347Spst	= {
66722347Spst		cmd_type = TYPE_A;
66822347Spst		cmd_form = FORM_N;
66922347Spst	}
67022347Spst	|	A SP form_code
67122347Spst	= {
67222347Spst		cmd_type = TYPE_A;
67322347Spst		cmd_form = $3;
67422347Spst	}
67522347Spst	|	E
67622347Spst	= {
67722347Spst		cmd_type = TYPE_E;
67822347Spst		cmd_form = FORM_N;
67922347Spst	}
68022347Spst	|	E SP form_code
68122347Spst	= {
68222347Spst		cmd_type = TYPE_E;
68322347Spst		cmd_form = $3;
68422347Spst	}
68522347Spst	|	I
68622347Spst	= {
68722347Spst		cmd_type = TYPE_I;
68822347Spst	}
68922347Spst	|	L
69022347Spst	= {
69122347Spst		cmd_type = TYPE_L;
69222347Spst		cmd_bytesz = NBBY;
69322347Spst	}
69422347Spst	|	L SP byte_size
69522347Spst	= {
69622347Spst		cmd_type = TYPE_L;
69722347Spst		cmd_bytesz = $3;
69822347Spst	}
69922347Spst	/* this is for a bug in the BBN ftp */
70022347Spst	|	L byte_size
70122347Spst	= {
70222347Spst		cmd_type = TYPE_L;
70322347Spst		cmd_bytesz = $2;
70422347Spst	}
70522347Spst	;
70622347Spst
70722347Spststruct_code:	F
70822347Spst	= {
70922347Spst		$$ = STRU_F;
71022347Spst	}
71122347Spst	|	R
71222347Spst	= {
71322347Spst		$$ = STRU_R;
71422347Spst	}
71522347Spst	|	P
71622347Spst	= {
71722347Spst		$$ = STRU_P;
71822347Spst	}
71922347Spst	;
72022347Spst
72122347Spstmode_code:	S
72222347Spst	= {
72322347Spst		$$ = MODE_S;
72422347Spst	}
72522347Spst	|	B
72622347Spst	= {
72722347Spst		$$ = MODE_B;
72822347Spst	}
72922347Spst	|	C
73022347Spst	= {
73122347Spst		$$ = MODE_C;
73222347Spst	}
73322347Spst	;
73422347Spst
73522347Spstpathname:	pathstring
73622347Spst	= {
73722347Spst		/*
73822347Spst		 * Problem: this production is used for all pathname
73922347Spst		 * processing, but only gives a 550 error reply.
74022347Spst		 * This is a valid reply in some cases but not in others.
74122347Spst		 */
74222347Spst		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
74322347Spst			*(char **)&($$) = *ftpglob((char *) $1);
74422347Spst			if (globerr != NULL) {
74522347Spst				reply(550, globerr);
74622347Spst/*				$$ = NULL; */
74722347Spst				$$ = 0;
74822347Spst			}
74922347Spst			free((char *) $1);
75022347Spst		} else
75122347Spst			$$ = $1;
75222347Spst	}
75322347Spst	;
75422347Spst
75522347Spstpathstring:	STRING
75622347Spst	;
75722347Spst
75822347Spstoctal_number:	NUMBER
75922347Spst	= {
76022347Spst		register int ret, dec, multby, digit;
76122347Spst
76222347Spst		/*
76322347Spst		 * Convert a number that was read as decimal number
76422347Spst		 * to what it would be if it had been read as octal.
76522347Spst		 */
76622347Spst		dec = $1;
76722347Spst		multby = 1;
76822347Spst		ret = 0;
76922347Spst		while (dec) {
77022347Spst			digit = dec%10;
77122347Spst			if (digit > 7) {
77222347Spst				ret = -1;
77322347Spst				break;
77422347Spst			}
77522347Spst			ret += digit * multby;
77622347Spst			multby *= 8;
77722347Spst			dec /= 10;
77822347Spst		}
77922347Spst		$$ = ret;
78022347Spst	}
78122347Spst	;
78222347Spst
78322347Spstcheck_login:	/* empty */
78422347Spst	= {
78522347Spst		if (logged_in)
78622347Spst			$$ = 1;
78722347Spst		else {
78822347Spst			reply(530, "Please login with USER and PASS.");
78922347Spst			$$ = 0;
79022347Spst		}
79122347Spst	}
79222347Spst	;
79322347Spst
79422347Spst%%
79522347Spst
79622347Spstextern jmp_buf errcatch;
79722347Spst
79822347Spst#define	CMD	0	/* beginning of command */
79922347Spst#define	ARGS	1	/* expect miscellaneous arguments */
80022347Spst#define	STR1	2	/* expect SP followed by STRING */
80122347Spst#define	STR2	3	/* expect STRING */
80222347Spst#define	OSTR	4	/* optional SP then STRING */
80322347Spst#define	ZSTR1	5	/* SP then optional STRING */
80422347Spst#define	ZSTR2	6	/* optional STRING after SP */
80522347Spst#define	SITECMD	7	/* SITE command */
80622347Spst#define	NSTR	8	/* Number followed by a string */
80722347Spst
80822347Spststruct tab cmdtab[] = {		/* In order defined in RFC 765 */
80922347Spst	{ "USER", USER, STR1, 1,	"<sp> username" },
81022347Spst	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
81122347Spst	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
81222347Spst	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
81322347Spst	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
81422347Spst	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
81522347Spst	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
81622347Spst	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
81722347Spst	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
81822347Spst	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
81922347Spst	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
82022347Spst	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
82122347Spst	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
82222347Spst	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
82322347Spst	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
82422347Spst	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
82522347Spst	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
82622347Spst	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
82722347Spst	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
82822347Spst	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
82922347Spst	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
83022347Spst	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
83122347Spst	{ "REST", REST, ARGS, 1,	"(restart command)" },
83222347Spst	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
83322347Spst	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
83422347Spst	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
83522347Spst	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
83622347Spst	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
83722347Spst	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
83822347Spst	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
83922347Spst	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
84022347Spst	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
84122347Spst	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
84222347Spst	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
84322347Spst	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
84422347Spst	{ "NOOP", NOOP, ARGS, 1,	"" },
84522347Spst	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
84622347Spst	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
84722347Spst	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
84822347Spst	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
84922347Spst	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
85022347Spst	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
85122347Spst	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
85222347Spst	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
85322347Spst	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
85422347Spst	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
85522347Spst	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
85622347Spst	{ NULL,   0,    0,    0,	0 }
85722347Spst};
85822347Spst
85922347Spststruct tab sitetab[] = {
86022347Spst	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
86122347Spst	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
86222347Spst	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
86322347Spst	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
86422347Spst	{ NULL,   0,    0,    0,	0 }
86522347Spst};
86622347Spst
86722347Spststruct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd)
86822347Spst{
86922347Spst
87022347Spst	for (; p->name != NULL; p++)
87122347Spst		if (strcmp(cmd, p->name) == 0)
87222347Spst			return (p);
87322347Spst	return (0);
87422347Spst}
87522347Spst
87622347Spst#include <arpa/telnet.h>
87722347Spst
87822347Spst/*
87922347Spst * getline - a hacked up version of fgets to ignore TELNET escape codes.
88022347Spst */
88122347Spstchar *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop)
88222347Spst{
88322347Spst	register c;
88422347Spst	register char *cs;
88522347Spst
88622347Spst	cs = s;
88722347Spst/* tmpline may contain saved command from urgent mode interruption */
88822347Spst	for (c = 0; *(tmpline + c) && --n > 0; ++c) {
88922347Spst		*cs++ = *(tmpline + c);
89022347Spst		if (*(tmpline + c) == '\n') {
89122347Spst			*cs++ = '\0';
89222347Spst			if (debug)
89322347Spst				syslog(LOG_DEBUG, "command: %s", s);
89422347Spst			*tmpline = '\0';
89522347Spst			return(s);
89622347Spst		}
89722347Spst		if (c == 0)
89822347Spst			*tmpline = '\0';
89922347Spst	}
90022347Spst	while ((c = getc(iop)) != EOF) {
90122347Spst		c &= 0377;
90222347Spst		if (c == IAC) {
90322347Spst		    if ((c = getc(iop)) != EOF) {
90422347Spst			c &= 0377;
90522347Spst			switch (c) {
90622347Spst			case WILL:
90722347Spst			case WONT:
90822347Spst				c = getc(iop);
90922347Spst				printf("%c%c%c", IAC, DONT, 0377&c);
91022347Spst				(void) fflush(stdout);
91122347Spst				continue;
91222347Spst			case DO:
91322347Spst			case DONT:
91422347Spst				c = getc(iop);
91522347Spst				printf("%c%c%c", IAC, WONT, 0377&c);
91622347Spst				(void) fflush(stdout);
91722347Spst				continue;
91822347Spst			case IAC:
91922347Spst				break;
92022347Spst			default:
92122347Spst				continue;	/* ignore command */
92222347Spst			}
92322347Spst		    }
92422347Spst		}
92522347Spst		*cs++ = c;
92622347Spst		if (--n <= 0 || c == '\n')
92722347Spst			break;
92822347Spst	}
92922347Spst	if (c == EOF && cs == s)
93022347Spst		return (NULL);
93122347Spst	*cs++ = '\0';
93222347Spst	if (debug)
93322347Spst		syslog(LOG_DEBUG, "command: %s", s);
93422347Spst	return (s);
93522347Spst}
93622347Spst
93722347Spststatic VOIDRET toolong FUNCTION((input), int input)
93822347Spst{
93922347Spst	time_t now;
94022347Spst
94122347Spst	reply(421, "Timeout (%d seconds): closing control connection.", timeout);
94222347Spst	(void) time(&now);
94322347Spst        syslog(LOG_INFO, "User %s timed out after %d seconds at %s",
94422347Spst          (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
94522347Spst	dologout(1);
94622347Spst}
94722347Spst
94822347Spstint yylex FUNCTION_NOARGS
94922347Spst{
95022347Spst	static int cpos, state;
95122347Spst	register char *cp, *cp2;
95222347Spst	register struct tab *p;
95322347Spst	int n;
95422347Spst	char c, *copy();
95522347Spst
95622347Spst	for (;;) {
95722347Spst		switch (state) {
95822347Spst
95922347Spst		case CMD:
96022347Spst			(void) signal(SIGALRM, toolong);
96122347Spst			(void) alarm((unsigned) timeout);
96222347Spst			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
96322347Spst				reply(221, "You could at least say goodbye.");
96422347Spst				dologout(0);
96522347Spst			}
96622347Spst			(void) alarm(0);
96722347Spst#ifdef SETPROCTITLE
96822347Spst			if (strncasecmp(cbuf, "PASS", 4) != NULL)
96922347Spst				setproctitle("%s: %s", proctitle, cbuf);
97022347Spst#endif /* SETPROCTITLE */
97122347Spst			if ((cp = strchr(cbuf, '\r'))) {
97222347Spst				*cp++ = '\n';
97322347Spst				*cp = '\0';
97422347Spst			}
97522347Spst			if ((cp = strpbrk(cbuf, " \n")))
97622347Spst				cpos = cp - cbuf;
97722347Spst			if (cpos == 0)
97822347Spst				cpos = 4;
97922347Spst			c = cbuf[cpos];
98022347Spst			cbuf[cpos] = '\0';
98122347Spst			upper(cbuf);
98222347Spst			p = lookup(cmdtab, cbuf);
98322347Spst			cbuf[cpos] = c;
98422347Spst			if (p != 0) {
98522347Spst				if (p->implemented == 0) {
98622347Spst					nack(p->name);
98722347Spst					longjmp(errcatch,0);
98822347Spst					/* NOTREACHED */
98922347Spst				}
99022347Spst				state = p->state;
99122347Spst				*(char **)&yylval = p->name;
99222347Spst				return (p->token);
99322347Spst			}
99422347Spst			break;
99522347Spst
99622347Spst		case SITECMD:
99722347Spst			if (cbuf[cpos] == ' ') {
99822347Spst				cpos++;
99922347Spst				return (SP);
100022347Spst			}
100122347Spst			cp = &cbuf[cpos];
100222347Spst			if ((cp2 = strpbrk(cp, " \n")))
100322347Spst				cpos = cp2 - cbuf;
100422347Spst			c = cbuf[cpos];
100522347Spst			cbuf[cpos] = '\0';
100622347Spst			upper(cp);
100722347Spst			p = lookup(sitetab, cp);
100822347Spst			cbuf[cpos] = c;
100922347Spst			if (p != 0) {
101022347Spst				if (p->implemented == 0) {
101122347Spst					state = CMD;
101222347Spst					nack(p->name);
101322347Spst					longjmp(errcatch,0);
101422347Spst					/* NOTREACHED */
101522347Spst				}
101622347Spst				state = p->state;
101722347Spst				*(char **)&yylval = p->name;
101822347Spst				return (p->token);
101922347Spst			}
102022347Spst			state = CMD;
102122347Spst			break;
102222347Spst
102322347Spst		case OSTR:
102422347Spst			if (cbuf[cpos] == '\n') {
102522347Spst				state = CMD;
102622347Spst				return (CRLF);
102722347Spst			}
102822347Spst			/* FALLTHROUGH */
102922347Spst
103022347Spst		case STR1:
103122347Spst		case ZSTR1:
103222347Spst		dostr1:
103322347Spst			if (cbuf[cpos] == ' ') {
103422347Spst				cpos++;
103522347Spst				state = state == OSTR ? STR2 : ++state;
103622347Spst				return (SP);
103722347Spst			}
103822347Spst			break;
103922347Spst
104022347Spst		case ZSTR2:
104122347Spst			if (cbuf[cpos] == '\n') {
104222347Spst				state = CMD;
104322347Spst				return (CRLF);
104422347Spst			}
104522347Spst			/* FALLTHROUGH */
104622347Spst
104722347Spst		case STR2:
104822347Spst			cp = &cbuf[cpos];
104922347Spst			n = strlen(cp);
105022347Spst			cpos += n - 1;
105122347Spst			/*
105222347Spst			 * Make sure the string is nonempty and \n terminated.
105322347Spst			 */
105422347Spst			if (n > 1 && cbuf[cpos] == '\n') {
105522347Spst				cbuf[cpos] = '\0';
105622347Spst				*(char **)&yylval = copy(cp);
105722347Spst				cbuf[cpos] = '\n';
105822347Spst				state = ARGS;
105922347Spst				return (STRING);
106022347Spst			}
106122347Spst			break;
106222347Spst
106322347Spst		case NSTR:
106422347Spst			if (cbuf[cpos] == ' ') {
106522347Spst				cpos++;
106622347Spst				return (SP);
106722347Spst			}
106822347Spst			if (isdigit(cbuf[cpos])) {
106922347Spst				cp = &cbuf[cpos];
107022347Spst				while (isdigit(cbuf[++cpos]))
107122347Spst					;
107222347Spst				c = cbuf[cpos];
107322347Spst				cbuf[cpos] = '\0';
107422347Spst				yylval = atoi(cp);
107522347Spst				cbuf[cpos] = c;
107622347Spst				state = STR1;
107722347Spst				return (NUMBER);
107822347Spst			}
107922347Spst			state = STR1;
108022347Spst			goto dostr1;
108122347Spst
108222347Spst		case ARGS:
108322347Spst			if (isdigit(cbuf[cpos])) {
108422347Spst				cp = &cbuf[cpos];
108522347Spst				while (isdigit(cbuf[++cpos]))
108622347Spst					;
108722347Spst				c = cbuf[cpos];
108822347Spst				cbuf[cpos] = '\0';
108922347Spst				yylval = atoi(cp);
109022347Spst				cbuf[cpos] = c;
109122347Spst				return (NUMBER);
109222347Spst			}
109322347Spst			switch (cbuf[cpos++]) {
109422347Spst
109522347Spst			case '\n':
109622347Spst				state = CMD;
109722347Spst				return (CRLF);
109822347Spst
109922347Spst			case ' ':
110022347Spst				return (SP);
110122347Spst
110222347Spst			case ',':
110322347Spst				return (COMMA);
110422347Spst
110522347Spst			case 'A':
110622347Spst			case 'a':
110722347Spst				return (A);
110822347Spst
110922347Spst			case 'B':
111022347Spst			case 'b':
111122347Spst				return (B);
111222347Spst
111322347Spst			case 'C':
111422347Spst			case 'c':
111522347Spst				return (C);
111622347Spst
111722347Spst			case 'E':
111822347Spst			case 'e':
111922347Spst				return (E);
112022347Spst
112122347Spst			case 'F':
112222347Spst			case 'f':
112322347Spst				return (F);
112422347Spst
112522347Spst			case 'I':
112622347Spst			case 'i':
112722347Spst				return (I);
112822347Spst
112922347Spst			case 'L':
113022347Spst			case 'l':
113122347Spst				return (L);
113222347Spst
113322347Spst			case 'N':
113422347Spst			case 'n':
113522347Spst				return (N);
113622347Spst
113722347Spst			case 'P':
113822347Spst			case 'p':
113922347Spst				return (P);
114022347Spst
114122347Spst			case 'R':
114222347Spst			case 'r':
114322347Spst				return (R);
114422347Spst
114522347Spst			case 'S':
114622347Spst			case 's':
114722347Spst				return (S);
114822347Spst
114922347Spst			case 'T':
115022347Spst			case 't':
115122347Spst				return (T);
115222347Spst
115322347Spst			}
115422347Spst			break;
115522347Spst
115622347Spst		default:
115722347Spst			opiefatal("Unknown state in scanner.");
115822347Spst		}
115922347Spst		yyerror((char *) 0);
116022347Spst		state = CMD;
116122347Spst		longjmp(errcatch,0);
116222347Spst	}
116322347Spst}
116422347Spst
116522347SpstVOIDRET upper FUNCTION((s), char *s)
116622347Spst{
116722347Spst	while (*s != '\0') {
116822347Spst		if (islower(*s))
116922347Spst			*s = toupper(*s);
117022347Spst		s++;
117122347Spst	}
117222347Spst}
117322347Spst
117422347Spstchar *copy FUNCTION((s), char *s)
117522347Spst{
117622347Spst	char *p;
117722347Spst
117822347Spst	p = malloc((unsigned) strlen(s) + 1);
117922347Spst	if (p == NULL)
118022347Spst		opiefatal("Ran out of memory.");
118122347Spst	(void) strcpy(p, s);
118222347Spst	return (p);
118322347Spst}
118422347Spst
118522347SpstVOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s)
118622347Spst{
118722347Spst	register struct tab *c;
118822347Spst	register int width, NCMDS;
118922347Spst	char *type;
119022347Spst
119122347Spst	if (ctab == sitetab)
119222347Spst		type = "SITE ";
119322347Spst	else
119422347Spst		type = "";
119522347Spst	width = 0, NCMDS = 0;
119622347Spst	for (c = ctab; c->name != NULL; c++) {
119722347Spst		int len = strlen(c->name);
119822347Spst
119922347Spst		if (len > width)
120022347Spst			width = len;
120122347Spst		NCMDS++;
120222347Spst	}
120322347Spst	width = (width + 8) &~ 7;
120422347Spst	if (s == 0) {
120522347Spst		register int i, j, w;
120622347Spst		int columns, lines;
120722347Spst
120822347Spst		lreply(214, "The following %scommands are recognized %s.",
120922347Spst		    type, "(* =>'s unimplemented)");
121022347Spst		columns = 76 / width;
121122347Spst		if (columns == 0)
121222347Spst			columns = 1;
121322347Spst		lines = (NCMDS + columns - 1) / columns;
121422347Spst		for (i = 0; i < lines; i++) {
121522347Spst			printf("   ");
121622347Spst			for (j = 0; j < columns; j++) {
121722347Spst				c = ctab + j * lines + i;
121822347Spst				printf("%s%c", c->name,
121922347Spst					c->implemented ? ' ' : '*');
122022347Spst				if (c + lines >= &ctab[NCMDS])
122122347Spst					break;
122222347Spst				w = strlen(c->name) + 1;
122322347Spst				while (w < width) {
122422347Spst					putchar(' ');
122522347Spst					w++;
122622347Spst				}
122722347Spst			}
122822347Spst			printf("\r\n");
122922347Spst		}
123022347Spst		(void) fflush(stdout);
123129964Sache		reply(214, " ");
123222347Spst		return;
123322347Spst	}
123422347Spst	upper(s);
123522347Spst	c = lookup(ctab, s);
123622347Spst	if (c == (struct tab *)0) {
123722347Spst		reply(502, "Unknown command %s.", s);
123822347Spst		return;
123922347Spst	}
124022347Spst	if (c->implemented)
124122347Spst		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
124222347Spst	else
124322347Spst		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
124422347Spst		    c->name, c->help);
124522347Spst}
124622347Spst
124722347SpstVOIDRET sizecmd FUNCTION((filename), char *filename)
124822347Spst{
124922347Spst	switch (type) {
125022347Spst	case TYPE_L:
125122347Spst	case TYPE_I: {
125222347Spst		struct stat stbuf;
125322347Spst		if (stat(filename, &stbuf) < 0 ||
125422347Spst		    (stbuf.st_mode&S_IFMT) != S_IFREG)
125522347Spst			reply(550, "%s: not a plain file.", filename);
125622347Spst		else
125722347Spst			reply(213, "%lu", stbuf.st_size);
125822347Spst		break;}
125922347Spst	case TYPE_A: {
126022347Spst		FILE *fin;
126122347Spst		register int c;
126222347Spst		register long count;
126322347Spst		struct stat stbuf;
126422347Spst		fin = fopen(filename, "r");
126522347Spst		if (fin == NULL) {
126622347Spst			perror_reply(550, filename);
126722347Spst			return;
126822347Spst		}
126922347Spst		if (fstat(fileno(fin), &stbuf) < 0 ||
127022347Spst		    (stbuf.st_mode&S_IFMT) != S_IFREG) {
127122347Spst			reply(550, "%s: not a plain file.", filename);
127222347Spst			(void) fclose(fin);
127322347Spst			return;
127422347Spst		}
127522347Spst
127622347Spst		count = 0;
127722347Spst		while((c=getc(fin)) != EOF) {
127822347Spst			if (c == '\n')	/* will get expanded to \r\n */
127922347Spst				count++;
128022347Spst			count++;
128122347Spst		}
128222347Spst		(void) fclose(fin);
128322347Spst
128422347Spst		reply(213, "%ld", count);
128522347Spst		break;}
128622347Spst	default:
128722347Spst		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
128822347Spst	}
128922347Spst}
1290