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