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