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