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