ftpcmd.y revision 29574
11592Srgrimes/* 21592Srgrimes * Copyright (c) 1985, 1988, 1993, 1994 31592Srgrimes * The Regents of the University of California. All rights reserved. 41592Srgrimes * 51592Srgrimes * Redistribution and use in source and binary forms, with or without 61592Srgrimes * modification, are permitted provided that the following conditions 71592Srgrimes * are met: 81592Srgrimes * 1. Redistributions of source code must retain the above copyright 91592Srgrimes * notice, this list of conditions and the following disclaimer. 101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111592Srgrimes * notice, this list of conditions and the following disclaimer in the 121592Srgrimes * documentation and/or other materials provided with the distribution. 131592Srgrimes * 3. All advertising materials mentioning features or use of this software 141592Srgrimes * must display the following acknowledgement: 151592Srgrimes * This product includes software developed by the University of 161592Srgrimes * California, Berkeley and its contributors. 171592Srgrimes * 4. Neither the name of the University nor the names of its contributors 181592Srgrimes * may be used to endorse or promote products derived from this software 191592Srgrimes * without specific prior written permission. 201592Srgrimes * 211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311592Srgrimes * SUCH DAMAGE. 321592Srgrimes * 331592Srgrimes * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 3429574Sphk * $Id: ftpcmd.y,v 1.11 1997/07/24 09:26:10 davidn Exp $ 351592Srgrimes */ 361592Srgrimes 371592Srgrimes/* 381592Srgrimes * Grammar for FTP commands. 391592Srgrimes * See RFC 959. 401592Srgrimes */ 411592Srgrimes 421592Srgrimes%{ 431592Srgrimes 441592Srgrimes#ifndef lint 451592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 461592Srgrimes#endif /* not lint */ 471592Srgrimes 481592Srgrimes#include <sys/param.h> 491592Srgrimes#include <sys/socket.h> 501592Srgrimes#include <sys/stat.h> 511592Srgrimes 521592Srgrimes#include <netinet/in.h> 531592Srgrimes#include <arpa/ftp.h> 541592Srgrimes 551592Srgrimes#include <ctype.h> 561592Srgrimes#include <errno.h> 571592Srgrimes#include <glob.h> 581592Srgrimes#include <pwd.h> 591592Srgrimes#include <setjmp.h> 601592Srgrimes#include <signal.h> 611592Srgrimes#include <stdio.h> 621592Srgrimes#include <stdlib.h> 631592Srgrimes#include <string.h> 641592Srgrimes#include <syslog.h> 651592Srgrimes#include <time.h> 661592Srgrimes#include <unistd.h> 6713139Speter#include <libutil.h> 681592Srgrimes 691592Srgrimes#include "extern.h" 701592Srgrimes 7117433Spstextern struct sockaddr_in data_dest, his_addr; 721592Srgrimesextern int logged_in; 731592Srgrimesextern struct passwd *pw; 741592Srgrimesextern int guest; 7517435Spstextern int paranoid; 761592Srgrimesextern int logging; 771592Srgrimesextern int type; 781592Srgrimesextern int form; 791592Srgrimesextern int debug; 801592Srgrimesextern int timeout; 811592Srgrimesextern int maxtimeout; 821592Srgrimesextern int pdata; 8327650Sdavidnextern char *hostname; 8427650Sdavidnextern char remotehost[]; 851592Srgrimesextern char proctitle[]; 861592Srgrimesextern int usedefault; 871592Srgrimesextern int transflag; 881592Srgrimesextern char tmpline[]; 891592Srgrimes 901592Srgrimesoff_t restart_point; 911592Srgrimes 921592Srgrimesstatic int cmd_type; 931592Srgrimesstatic int cmd_form; 941592Srgrimesstatic int cmd_bytesz; 951592Srgrimeschar cbuf[512]; 961592Srgrimeschar *fromname; 971592Srgrimes 981592Srgrimes%} 991592Srgrimes 1001592Srgrimes%union { 1011592Srgrimes int i; 1021592Srgrimes char *s; 1031592Srgrimes} 1041592Srgrimes 1051592Srgrimes%token 1061592Srgrimes A B C E F I 1071592Srgrimes L N P R S T 1081592Srgrimes 1091592Srgrimes SP CRLF COMMA 1101592Srgrimes 1111592Srgrimes USER PASS ACCT REIN QUIT PORT 1121592Srgrimes PASV TYPE STRU MODE RETR STOR 1131592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1141592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1151592Srgrimes ABOR DELE CWD LIST NLST SITE 1161592Srgrimes STAT HELP NOOP MKD RMD PWD 1171592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 1181592Srgrimes 1191592Srgrimes UMASK IDLE CHMOD 1201592Srgrimes 1211592Srgrimes LEXERR 1221592Srgrimes 1231592Srgrimes%token <s> STRING 1241592Srgrimes%token <i> NUMBER 1251592Srgrimes 1261592Srgrimes%type <i> check_login octal_number byte_size 1271592Srgrimes%type <i> struct_code mode_code type_code form_code 1281592Srgrimes%type <s> pathstring pathname password username 1291592Srgrimes 1301592Srgrimes%start cmd_list 1311592Srgrimes 1321592Srgrimes%% 1331592Srgrimes 1341592Srgrimescmd_list 1351592Srgrimes : /* empty */ 1361592Srgrimes | cmd_list cmd 1371592Srgrimes { 1381592Srgrimes fromname = (char *) 0; 1391592Srgrimes restart_point = (off_t) 0; 1401592Srgrimes } 1411592Srgrimes | cmd_list rcmd 1421592Srgrimes ; 1431592Srgrimes 1441592Srgrimescmd 1451592Srgrimes : USER SP username CRLF 1461592Srgrimes { 1471592Srgrimes user($3); 1481592Srgrimes free($3); 1491592Srgrimes } 1501592Srgrimes | PASS SP password CRLF 1511592Srgrimes { 1521592Srgrimes pass($3); 1531592Srgrimes free($3); 1541592Srgrimes } 15517433Spst | PORT check_login SP host_port CRLF 1561592Srgrimes { 15717433Spst if ($2) { 15817435Spst if (paranoid && 15917435Spst ((ntohs(data_dest.sin_port) < 16017435Spst IPPORT_RESERVED) || 16117435Spst memcmp(&data_dest.sin_addr, 16217435Spst &his_addr.sin_addr, 16317435Spst sizeof(data_dest.sin_addr)))) { 16417433Spst usedefault = 1; 16517433Spst reply(500, 16617433Spst "Illegal PORT range rejected."); 16717435Spst } else { 16817433Spst usedefault = 0; 16917433Spst if (pdata >= 0) { 17017433Spst (void) close(pdata); 17117433Spst pdata = -1; 17217433Spst } 17317433Spst reply(200, "PORT command successful."); 17417433Spst } 1751592Srgrimes } 1761592Srgrimes } 17717433Spst | PASV check_login CRLF 1781592Srgrimes { 17917433Spst if ($2) 18017433Spst passive(); 1811592Srgrimes } 1821592Srgrimes | TYPE SP type_code CRLF 1831592Srgrimes { 1841592Srgrimes switch (cmd_type) { 1851592Srgrimes 1861592Srgrimes case TYPE_A: 1871592Srgrimes if (cmd_form == FORM_N) { 1881592Srgrimes reply(200, "Type set to A."); 1891592Srgrimes type = cmd_type; 1901592Srgrimes form = cmd_form; 1911592Srgrimes } else 1921592Srgrimes reply(504, "Form must be N."); 1931592Srgrimes break; 1941592Srgrimes 1951592Srgrimes case TYPE_E: 1961592Srgrimes reply(504, "Type E not implemented."); 1971592Srgrimes break; 1981592Srgrimes 1991592Srgrimes case TYPE_I: 2001592Srgrimes reply(200, "Type set to I."); 2011592Srgrimes type = cmd_type; 2021592Srgrimes break; 2031592Srgrimes 2041592Srgrimes case TYPE_L: 2051592Srgrimes#if NBBY == 8 2061592Srgrimes if (cmd_bytesz == 8) { 2071592Srgrimes reply(200, 2081592Srgrimes "Type set to L (byte size 8)."); 2091592Srgrimes type = cmd_type; 2101592Srgrimes } else 2111592Srgrimes reply(504, "Byte size must be 8."); 2121592Srgrimes#else /* NBBY == 8 */ 2131592Srgrimes UNIMPLEMENTED for NBBY != 8 2141592Srgrimes#endif /* NBBY == 8 */ 2151592Srgrimes } 2161592Srgrimes } 2171592Srgrimes | STRU SP struct_code CRLF 2181592Srgrimes { 2191592Srgrimes switch ($3) { 2201592Srgrimes 2211592Srgrimes case STRU_F: 2221592Srgrimes reply(200, "STRU F ok."); 2231592Srgrimes break; 2241592Srgrimes 2251592Srgrimes default: 2261592Srgrimes reply(504, "Unimplemented STRU type."); 2271592Srgrimes } 2281592Srgrimes } 2291592Srgrimes | MODE SP mode_code CRLF 2301592Srgrimes { 2311592Srgrimes switch ($3) { 2321592Srgrimes 2331592Srgrimes case MODE_S: 2341592Srgrimes reply(200, "MODE S ok."); 2351592Srgrimes break; 2361592Srgrimes 2371592Srgrimes default: 2381592Srgrimes reply(502, "Unimplemented MODE type."); 2391592Srgrimes } 2401592Srgrimes } 2411592Srgrimes | ALLO SP NUMBER CRLF 2421592Srgrimes { 2431592Srgrimes reply(202, "ALLO command ignored."); 2441592Srgrimes } 2451592Srgrimes | ALLO SP NUMBER SP R SP NUMBER CRLF 2461592Srgrimes { 2471592Srgrimes reply(202, "ALLO command ignored."); 2481592Srgrimes } 2491592Srgrimes | RETR check_login SP pathname CRLF 2501592Srgrimes { 2511592Srgrimes if ($2 && $4 != NULL) 2521592Srgrimes retrieve((char *) 0, $4); 2531592Srgrimes if ($4 != NULL) 2541592Srgrimes free($4); 2551592Srgrimes } 2561592Srgrimes | STOR check_login SP pathname CRLF 2571592Srgrimes { 2581592Srgrimes if ($2 && $4 != NULL) 2591592Srgrimes store($4, "w", 0); 2601592Srgrimes if ($4 != NULL) 2611592Srgrimes free($4); 2621592Srgrimes } 2631592Srgrimes | APPE check_login SP pathname CRLF 2641592Srgrimes { 2651592Srgrimes if ($2 && $4 != NULL) 2661592Srgrimes store($4, "a", 0); 2671592Srgrimes if ($4 != NULL) 2681592Srgrimes free($4); 2691592Srgrimes } 2701592Srgrimes | NLST check_login CRLF 2711592Srgrimes { 2721592Srgrimes if ($2) 2731592Srgrimes send_file_list("."); 2741592Srgrimes } 2751592Srgrimes | NLST check_login SP STRING CRLF 2761592Srgrimes { 2771592Srgrimes if ($2 && $4 != NULL) 2781592Srgrimes send_file_list($4); 2791592Srgrimes if ($4 != NULL) 2801592Srgrimes free($4); 2811592Srgrimes } 2821592Srgrimes | LIST check_login CRLF 2831592Srgrimes { 2841592Srgrimes if ($2) 2851592Srgrimes retrieve("/bin/ls -lgA", ""); 2861592Srgrimes } 2871592Srgrimes | LIST check_login SP pathname CRLF 2881592Srgrimes { 2891592Srgrimes if ($2 && $4 != NULL) 2901592Srgrimes retrieve("/bin/ls -lgA %s", $4); 2911592Srgrimes if ($4 != NULL) 2921592Srgrimes free($4); 2931592Srgrimes } 2941592Srgrimes | STAT check_login SP pathname CRLF 2951592Srgrimes { 2961592Srgrimes if ($2 && $4 != NULL) 2971592Srgrimes statfilecmd($4); 2981592Srgrimes if ($4 != NULL) 2991592Srgrimes free($4); 3001592Srgrimes } 3011592Srgrimes | STAT CRLF 3021592Srgrimes { 3031592Srgrimes statcmd(); 3041592Srgrimes } 3051592Srgrimes | DELE check_login SP pathname CRLF 3061592Srgrimes { 3071592Srgrimes if ($2 && $4 != NULL) 3081592Srgrimes delete($4); 3091592Srgrimes if ($4 != NULL) 3101592Srgrimes free($4); 3111592Srgrimes } 31217433Spst | RNTO check_login SP pathname CRLF 3131592Srgrimes { 31417433Spst if ($2) { 31517433Spst if (fromname) { 31617433Spst renamecmd(fromname, $4); 31717433Spst free(fromname); 31817433Spst fromname = (char *) 0; 31917433Spst } else { 32017433Spst reply(503, "Bad sequence of commands."); 32117433Spst } 3221592Srgrimes } 32317433Spst free($4); 3241592Srgrimes } 3251592Srgrimes | ABOR CRLF 3261592Srgrimes { 3271592Srgrimes reply(225, "ABOR command successful."); 3281592Srgrimes } 3291592Srgrimes | CWD check_login CRLF 3301592Srgrimes { 3311592Srgrimes if ($2) 3321592Srgrimes cwd(pw->pw_dir); 3331592Srgrimes } 3341592Srgrimes | CWD check_login SP pathname CRLF 3351592Srgrimes { 3361592Srgrimes if ($2 && $4 != NULL) 3371592Srgrimes cwd($4); 3381592Srgrimes if ($4 != NULL) 3391592Srgrimes free($4); 3401592Srgrimes } 3411592Srgrimes | HELP CRLF 3421592Srgrimes { 3431592Srgrimes help(cmdtab, (char *) 0); 3441592Srgrimes } 3451592Srgrimes | HELP SP STRING CRLF 3461592Srgrimes { 3471592Srgrimes char *cp = $3; 3481592Srgrimes 3491592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 3501592Srgrimes cp = $3 + 4; 3511592Srgrimes if (*cp == ' ') 3521592Srgrimes cp++; 3531592Srgrimes if (*cp) 3541592Srgrimes help(sitetab, cp); 3551592Srgrimes else 3561592Srgrimes help(sitetab, (char *) 0); 3571592Srgrimes } else 3581592Srgrimes help(cmdtab, $3); 3591592Srgrimes } 3601592Srgrimes | NOOP CRLF 3611592Srgrimes { 3621592Srgrimes reply(200, "NOOP command successful."); 3631592Srgrimes } 3641592Srgrimes | MKD check_login SP pathname CRLF 3651592Srgrimes { 3661592Srgrimes if ($2 && $4 != NULL) 3671592Srgrimes makedir($4); 3681592Srgrimes if ($4 != NULL) 3691592Srgrimes free($4); 3701592Srgrimes } 3711592Srgrimes | RMD check_login SP pathname CRLF 3721592Srgrimes { 3731592Srgrimes if ($2 && $4 != NULL) 3741592Srgrimes removedir($4); 3751592Srgrimes if ($4 != NULL) 3761592Srgrimes free($4); 3771592Srgrimes } 3781592Srgrimes | PWD check_login CRLF 3791592Srgrimes { 3801592Srgrimes if ($2) 3811592Srgrimes pwd(); 3821592Srgrimes } 3831592Srgrimes | CDUP check_login CRLF 3841592Srgrimes { 3851592Srgrimes if ($2) 3861592Srgrimes cwd(".."); 3871592Srgrimes } 3881592Srgrimes | SITE SP HELP CRLF 3891592Srgrimes { 3901592Srgrimes help(sitetab, (char *) 0); 3911592Srgrimes } 3921592Srgrimes | SITE SP HELP SP STRING CRLF 3931592Srgrimes { 3941592Srgrimes help(sitetab, $5); 3951592Srgrimes } 3961592Srgrimes | SITE SP UMASK check_login CRLF 3971592Srgrimes { 3981592Srgrimes int oldmask; 3991592Srgrimes 4001592Srgrimes if ($4) { 4011592Srgrimes oldmask = umask(0); 4021592Srgrimes (void) umask(oldmask); 4031592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 4041592Srgrimes } 4051592Srgrimes } 4061592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 4071592Srgrimes { 4081592Srgrimes int oldmask; 4091592Srgrimes 4101592Srgrimes if ($4) { 4111592Srgrimes if (($6 == -1) || ($6 > 0777)) { 4121592Srgrimes reply(501, "Bad UMASK value"); 4131592Srgrimes } else { 4141592Srgrimes oldmask = umask($6); 4151592Srgrimes reply(200, 4161592Srgrimes "UMASK set to %03o (was %03o)", 4171592Srgrimes $6, oldmask); 4181592Srgrimes } 4191592Srgrimes } 4201592Srgrimes } 4211592Srgrimes | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 4221592Srgrimes { 4231592Srgrimes if ($4 && ($8 != NULL)) { 4241592Srgrimes if ($6 > 0777) 4251592Srgrimes reply(501, 4261592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 4271592Srgrimes else if (chmod($8, $6) < 0) 4281592Srgrimes perror_reply(550, $8); 4291592Srgrimes else 4301592Srgrimes reply(200, "CHMOD command successful."); 4311592Srgrimes } 4321592Srgrimes if ($8 != NULL) 4331592Srgrimes free($8); 4341592Srgrimes } 4351592Srgrimes | SITE SP IDLE CRLF 4361592Srgrimes { 4371592Srgrimes reply(200, 4381592Srgrimes "Current IDLE time limit is %d seconds; max %d", 4391592Srgrimes timeout, maxtimeout); 4401592Srgrimes } 4411592Srgrimes | SITE SP IDLE SP NUMBER CRLF 4421592Srgrimes { 4431592Srgrimes if ($5 < 30 || $5 > maxtimeout) { 4441592Srgrimes reply(501, 4451592Srgrimes "Maximum IDLE time must be between 30 and %d seconds", 4461592Srgrimes maxtimeout); 4471592Srgrimes } else { 4481592Srgrimes timeout = $5; 4491592Srgrimes (void) alarm((unsigned) timeout); 4501592Srgrimes reply(200, 4511592Srgrimes "Maximum IDLE time set to %d seconds", 4521592Srgrimes timeout); 4531592Srgrimes } 4541592Srgrimes } 4551592Srgrimes | STOU check_login SP pathname CRLF 4561592Srgrimes { 4571592Srgrimes if ($2 && $4 != NULL) 4581592Srgrimes store($4, "w", 1); 4591592Srgrimes if ($4 != NULL) 4601592Srgrimes free($4); 4611592Srgrimes } 4621592Srgrimes | SYST CRLF 4631592Srgrimes { 4641592Srgrimes#ifdef unix 4651592Srgrimes#ifdef BSD 4661592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 4671592Srgrimes NBBY, BSD); 4681592Srgrimes#else /* BSD */ 4691592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 4701592Srgrimes#endif /* BSD */ 4711592Srgrimes#else /* unix */ 4721592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 4731592Srgrimes#endif /* unix */ 4741592Srgrimes } 4751592Srgrimes 4761592Srgrimes /* 4771592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 4781592Srgrimes * it will be in the updated RFC. 4791592Srgrimes * 4801592Srgrimes * Return size of file in a format suitable for 4811592Srgrimes * using with RESTART (we just count bytes). 4821592Srgrimes */ 4831592Srgrimes | SIZE check_login SP pathname CRLF 4841592Srgrimes { 4851592Srgrimes if ($2 && $4 != NULL) 4861592Srgrimes sizecmd($4); 4871592Srgrimes if ($4 != NULL) 4881592Srgrimes free($4); 4891592Srgrimes } 4901592Srgrimes 4911592Srgrimes /* 4921592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 4931592Srgrimes * it will be in the updated RFC. 4941592Srgrimes * 4951592Srgrimes * Return modification time of file as an ISO 3307 4961592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 4971592Srgrimes * where xxx is the fractional second (of any precision, 4981592Srgrimes * not necessarily 3 digits) 4991592Srgrimes */ 5001592Srgrimes | MDTM check_login SP pathname CRLF 5011592Srgrimes { 5021592Srgrimes if ($2 && $4 != NULL) { 5031592Srgrimes struct stat stbuf; 5041592Srgrimes if (stat($4, &stbuf) < 0) 5051592Srgrimes reply(550, "%s: %s", 5061592Srgrimes $4, strerror(errno)); 5071592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 5081592Srgrimes reply(550, "%s: not a plain file.", $4); 5091592Srgrimes } else { 5101592Srgrimes struct tm *t; 5111592Srgrimes t = gmtime(&stbuf.st_mtime); 5121592Srgrimes reply(213, 51317435Spst "%04d%02d%02d%02d%02d%02d", 51417435Spst 1900 + t->tm_year, 51517435Spst t->tm_mon+1, t->tm_mday, 5161592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 5171592Srgrimes } 5181592Srgrimes } 5191592Srgrimes if ($4 != NULL) 5201592Srgrimes free($4); 5211592Srgrimes } 5221592Srgrimes | QUIT CRLF 5231592Srgrimes { 5241592Srgrimes reply(221, "Goodbye."); 5251592Srgrimes dologout(0); 5261592Srgrimes } 5271592Srgrimes | error CRLF 5281592Srgrimes { 5291592Srgrimes yyerrok; 5301592Srgrimes } 5311592Srgrimes ; 5321592Srgrimesrcmd 5331592Srgrimes : RNFR check_login SP pathname CRLF 5341592Srgrimes { 5351592Srgrimes char *renamefrom(); 5361592Srgrimes 5371592Srgrimes restart_point = (off_t) 0; 5381592Srgrimes if ($2 && $4) { 5391592Srgrimes fromname = renamefrom($4); 5401592Srgrimes if (fromname == (char *) 0 && $4) { 5411592Srgrimes free($4); 5421592Srgrimes } 5431592Srgrimes } 5441592Srgrimes } 5451592Srgrimes | REST SP byte_size CRLF 5461592Srgrimes { 5471592Srgrimes fromname = (char *) 0; 5481592Srgrimes restart_point = $3; /* XXX $3 is only "int" */ 5491592Srgrimes reply(350, "Restarting at %qd. %s", restart_point, 5501592Srgrimes "Send STORE or RETRIEVE to initiate transfer."); 5511592Srgrimes } 5521592Srgrimes ; 5531592Srgrimes 5541592Srgrimesusername 5551592Srgrimes : STRING 5561592Srgrimes ; 5571592Srgrimes 5581592Srgrimespassword 5591592Srgrimes : /* empty */ 5601592Srgrimes { 5611592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 5621592Srgrimes } 5631592Srgrimes | STRING 5641592Srgrimes ; 5651592Srgrimes 5661592Srgrimesbyte_size 5671592Srgrimes : NUMBER 5681592Srgrimes ; 5691592Srgrimes 5701592Srgrimeshost_port 5711592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 5721592Srgrimes NUMBER COMMA NUMBER 5731592Srgrimes { 5741592Srgrimes char *a, *p; 5751592Srgrimes 57617435Spst data_dest.sin_len = sizeof(struct sockaddr_in); 57717435Spst data_dest.sin_family = AF_INET; 57817435Spst p = (char *)&data_dest.sin_port; 57917435Spst p[0] = $9; p[1] = $11; 5801592Srgrimes a = (char *)&data_dest.sin_addr; 5811592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 5821592Srgrimes } 5831592Srgrimes ; 5841592Srgrimes 5851592Srgrimesform_code 5861592Srgrimes : N 5871592Srgrimes { 5881592Srgrimes $$ = FORM_N; 5891592Srgrimes } 5901592Srgrimes | T 5911592Srgrimes { 5921592Srgrimes $$ = FORM_T; 5931592Srgrimes } 5941592Srgrimes | C 5951592Srgrimes { 5961592Srgrimes $$ = FORM_C; 5971592Srgrimes } 5981592Srgrimes ; 5991592Srgrimes 6001592Srgrimestype_code 6011592Srgrimes : A 6021592Srgrimes { 6031592Srgrimes cmd_type = TYPE_A; 6041592Srgrimes cmd_form = FORM_N; 6051592Srgrimes } 6061592Srgrimes | A SP form_code 6071592Srgrimes { 6081592Srgrimes cmd_type = TYPE_A; 6091592Srgrimes cmd_form = $3; 6101592Srgrimes } 6111592Srgrimes | E 6121592Srgrimes { 6131592Srgrimes cmd_type = TYPE_E; 6141592Srgrimes cmd_form = FORM_N; 6151592Srgrimes } 6161592Srgrimes | E SP form_code 6171592Srgrimes { 6181592Srgrimes cmd_type = TYPE_E; 6191592Srgrimes cmd_form = $3; 6201592Srgrimes } 6211592Srgrimes | I 6221592Srgrimes { 6231592Srgrimes cmd_type = TYPE_I; 6241592Srgrimes } 6251592Srgrimes | L 6261592Srgrimes { 6271592Srgrimes cmd_type = TYPE_L; 6281592Srgrimes cmd_bytesz = NBBY; 6291592Srgrimes } 6301592Srgrimes | L SP byte_size 6311592Srgrimes { 6321592Srgrimes cmd_type = TYPE_L; 6331592Srgrimes cmd_bytesz = $3; 6341592Srgrimes } 6351592Srgrimes /* this is for a bug in the BBN ftp */ 6361592Srgrimes | L byte_size 6371592Srgrimes { 6381592Srgrimes cmd_type = TYPE_L; 6391592Srgrimes cmd_bytesz = $2; 6401592Srgrimes } 6411592Srgrimes ; 6421592Srgrimes 6431592Srgrimesstruct_code 6441592Srgrimes : F 6451592Srgrimes { 6461592Srgrimes $$ = STRU_F; 6471592Srgrimes } 6481592Srgrimes | R 6491592Srgrimes { 6501592Srgrimes $$ = STRU_R; 6511592Srgrimes } 6521592Srgrimes | P 6531592Srgrimes { 6541592Srgrimes $$ = STRU_P; 6551592Srgrimes } 6561592Srgrimes ; 6571592Srgrimes 6581592Srgrimesmode_code 6591592Srgrimes : S 6601592Srgrimes { 6611592Srgrimes $$ = MODE_S; 6621592Srgrimes } 6631592Srgrimes | B 6641592Srgrimes { 6651592Srgrimes $$ = MODE_B; 6661592Srgrimes } 6671592Srgrimes | C 6681592Srgrimes { 6691592Srgrimes $$ = MODE_C; 6701592Srgrimes } 6711592Srgrimes ; 6721592Srgrimes 6731592Srgrimespathname 6741592Srgrimes : pathstring 6751592Srgrimes { 6761592Srgrimes /* 6771592Srgrimes * Problem: this production is used for all pathname 6781592Srgrimes * processing, but only gives a 550 error reply. 6791592Srgrimes * This is a valid reply in some cases but not in others. 6801592Srgrimes */ 6811592Srgrimes if (logged_in && $1 && *$1 == '~') { 6821592Srgrimes glob_t gl; 6831592Srgrimes int flags = 6841592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 6851592Srgrimes 6861592Srgrimes memset(&gl, 0, sizeof(gl)); 6871592Srgrimes if (glob($1, flags, NULL, &gl) || 6881592Srgrimes gl.gl_pathc == 0) { 6891592Srgrimes reply(550, "not found"); 6901592Srgrimes $$ = NULL; 6911592Srgrimes } else { 6921592Srgrimes $$ = strdup(gl.gl_pathv[0]); 6931592Srgrimes } 6941592Srgrimes globfree(&gl); 6951592Srgrimes free($1); 6961592Srgrimes } else 6971592Srgrimes $$ = $1; 6981592Srgrimes } 6991592Srgrimes ; 7001592Srgrimes 7011592Srgrimespathstring 7021592Srgrimes : STRING 7031592Srgrimes ; 7041592Srgrimes 7051592Srgrimesoctal_number 7061592Srgrimes : NUMBER 7071592Srgrimes { 7081592Srgrimes int ret, dec, multby, digit; 7091592Srgrimes 7101592Srgrimes /* 7111592Srgrimes * Convert a number that was read as decimal number 7121592Srgrimes * to what it would be if it had been read as octal. 7131592Srgrimes */ 7141592Srgrimes dec = $1; 7151592Srgrimes multby = 1; 7161592Srgrimes ret = 0; 7171592Srgrimes while (dec) { 7181592Srgrimes digit = dec%10; 7191592Srgrimes if (digit > 7) { 7201592Srgrimes ret = -1; 7211592Srgrimes break; 7221592Srgrimes } 7231592Srgrimes ret += digit * multby; 7241592Srgrimes multby *= 8; 7251592Srgrimes dec /= 10; 7261592Srgrimes } 7271592Srgrimes $$ = ret; 7281592Srgrimes } 7291592Srgrimes ; 7301592Srgrimes 7311592Srgrimes 7321592Srgrimescheck_login 7331592Srgrimes : /* empty */ 7341592Srgrimes { 7351592Srgrimes if (logged_in) 7361592Srgrimes $$ = 1; 7371592Srgrimes else { 7381592Srgrimes reply(530, "Please login with USER and PASS."); 7391592Srgrimes $$ = 0; 7401592Srgrimes } 7411592Srgrimes } 7421592Srgrimes ; 7431592Srgrimes 7441592Srgrimes%% 7451592Srgrimes 7461592Srgrimesextern jmp_buf errcatch; 7471592Srgrimes 7481592Srgrimes#define CMD 0 /* beginning of command */ 7491592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 7501592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 7511592Srgrimes#define STR2 3 /* expect STRING */ 7521592Srgrimes#define OSTR 4 /* optional SP then STRING */ 7531592Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 7541592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 7551592Srgrimes#define SITECMD 7 /* SITE command */ 7561592Srgrimes#define NSTR 8 /* Number followed by a string */ 7571592Srgrimes 7581592Srgrimesstruct tab { 7591592Srgrimes char *name; 7601592Srgrimes short token; 7611592Srgrimes short state; 7621592Srgrimes short implemented; /* 1 if command is implemented */ 7631592Srgrimes char *help; 7641592Srgrimes}; 7651592Srgrimes 7661592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 7671592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 7681592Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 7691592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 7701592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 7711592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 7721592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 7731592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 7741592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 7751592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 7761592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 7771592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 7781592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 7791592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 7801592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 7811592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 7821592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 7831592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 7841592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 7851592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 7861592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 7871592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 7881592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 7891592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 7901592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 7911592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 7921592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 7931592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 7941592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 7951592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 7961592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 7971592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 7981592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 7991592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 8001592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 8011592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 8021592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 8031592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 8041592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 8051592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 8061592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 8071592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 8081592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 8091592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 8101592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 8111592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 8121592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 8131592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 8141592Srgrimes { NULL, 0, 0, 0, 0 } 8151592Srgrimes}; 8161592Srgrimes 8171592Srgrimesstruct tab sitetab[] = { 8181592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 8191592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 8201592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 8211592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 8221592Srgrimes { NULL, 0, 0, 0, 0 } 8231592Srgrimes}; 8241592Srgrimes 8251592Srgrimesstatic char *copy __P((char *)); 8261592Srgrimesstatic void help __P((struct tab *, char *)); 8271592Srgrimesstatic struct tab * 8281592Srgrimes lookup __P((struct tab *, char *)); 8291592Srgrimesstatic void sizecmd __P((char *)); 8301592Srgrimesstatic void toolong __P((int)); 8311592Srgrimesstatic int yylex __P((void)); 8321592Srgrimes 8331592Srgrimesstatic struct tab * 8341592Srgrimeslookup(p, cmd) 8351592Srgrimes struct tab *p; 8361592Srgrimes char *cmd; 8371592Srgrimes{ 8381592Srgrimes 8391592Srgrimes for (; p->name != NULL; p++) 8401592Srgrimes if (strcmp(cmd, p->name) == 0) 8411592Srgrimes return (p); 8421592Srgrimes return (0); 8431592Srgrimes} 8441592Srgrimes 8451592Srgrimes#include <arpa/telnet.h> 8461592Srgrimes 8471592Srgrimes/* 8481592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 8491592Srgrimes */ 8501592Srgrimeschar * 8511592Srgrimesgetline(s, n, iop) 8521592Srgrimes char *s; 8531592Srgrimes int n; 8541592Srgrimes FILE *iop; 8551592Srgrimes{ 8561592Srgrimes int c; 8571592Srgrimes register char *cs; 8581592Srgrimes 8591592Srgrimes cs = s; 8601592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 8611592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 8621592Srgrimes *cs++ = tmpline[c]; 8631592Srgrimes if (tmpline[c] == '\n') { 8641592Srgrimes *cs++ = '\0'; 8651592Srgrimes if (debug) 8661592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 8671592Srgrimes tmpline[0] = '\0'; 8681592Srgrimes return(s); 8691592Srgrimes } 8701592Srgrimes if (c == 0) 8711592Srgrimes tmpline[0] = '\0'; 8721592Srgrimes } 8731592Srgrimes while ((c = getc(iop)) != EOF) { 8741592Srgrimes c &= 0377; 8751592Srgrimes if (c == IAC) { 8761592Srgrimes if ((c = getc(iop)) != EOF) { 8771592Srgrimes c &= 0377; 8781592Srgrimes switch (c) { 8791592Srgrimes case WILL: 8801592Srgrimes case WONT: 8811592Srgrimes c = getc(iop); 8821592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 8831592Srgrimes (void) fflush(stdout); 8841592Srgrimes continue; 8851592Srgrimes case DO: 8861592Srgrimes case DONT: 8871592Srgrimes c = getc(iop); 8881592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 8891592Srgrimes (void) fflush(stdout); 8901592Srgrimes continue; 8911592Srgrimes case IAC: 8921592Srgrimes break; 8931592Srgrimes default: 8941592Srgrimes continue; /* ignore command */ 8951592Srgrimes } 8961592Srgrimes } 8971592Srgrimes } 8981592Srgrimes *cs++ = c; 8991592Srgrimes if (--n <= 0 || c == '\n') 9001592Srgrimes break; 9011592Srgrimes } 9021592Srgrimes if (c == EOF && cs == s) 9031592Srgrimes return (NULL); 9041592Srgrimes *cs++ = '\0'; 9051592Srgrimes if (debug) { 9061592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 9071592Srgrimes /* Don't syslog passwords */ 9081592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 9091592Srgrimes } else { 9101592Srgrimes register char *cp; 9111592Srgrimes register int len; 9121592Srgrimes 9131592Srgrimes /* Don't syslog trailing CR-LF */ 9141592Srgrimes len = strlen(s); 9151592Srgrimes cp = s + len - 1; 9161592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 9171592Srgrimes --cp; 9181592Srgrimes --len; 9191592Srgrimes } 9201592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 9211592Srgrimes } 9221592Srgrimes } 9231592Srgrimes return (s); 9241592Srgrimes} 9251592Srgrimes 9261592Srgrimesstatic void 9271592Srgrimestoolong(signo) 9281592Srgrimes int signo; 9291592Srgrimes{ 9301592Srgrimes 9311592Srgrimes reply(421, 9321592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 9331592Srgrimes if (logging) 9341592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 9351592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 9361592Srgrimes dologout(1); 9371592Srgrimes} 9381592Srgrimes 9391592Srgrimesstatic int 9401592Srgrimesyylex() 9411592Srgrimes{ 9421592Srgrimes static int cpos, state; 9431592Srgrimes char *cp, *cp2; 9441592Srgrimes struct tab *p; 9451592Srgrimes int n; 9461592Srgrimes char c; 9471592Srgrimes 9481592Srgrimes for (;;) { 9491592Srgrimes switch (state) { 9501592Srgrimes 9511592Srgrimes case CMD: 9521592Srgrimes (void) signal(SIGALRM, toolong); 9531592Srgrimes (void) alarm((unsigned) timeout); 9541592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 9551592Srgrimes reply(221, "You could at least say goodbye."); 9561592Srgrimes dologout(0); 9571592Srgrimes } 9581592Srgrimes (void) alarm(0); 9591592Srgrimes#ifdef SETPROCTITLE 96029574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 9611592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 9621592Srgrimes#endif /* SETPROCTITLE */ 9631592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 9641592Srgrimes *cp++ = '\n'; 9651592Srgrimes *cp = '\0'; 9661592Srgrimes } 9671592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 9681592Srgrimes cpos = cp - cbuf; 9691592Srgrimes if (cpos == 0) 9701592Srgrimes cpos = 4; 9711592Srgrimes c = cbuf[cpos]; 9721592Srgrimes cbuf[cpos] = '\0'; 9731592Srgrimes upper(cbuf); 9741592Srgrimes p = lookup(cmdtab, cbuf); 9751592Srgrimes cbuf[cpos] = c; 9763776Spst if (p != 0) { 9771592Srgrimes if (p->implemented == 0) { 9781592Srgrimes nack(p->name); 9791592Srgrimes longjmp(errcatch,0); 9801592Srgrimes /* NOTREACHED */ 9811592Srgrimes } 9821592Srgrimes state = p->state; 9831592Srgrimes yylval.s = p->name; 9841592Srgrimes return (p->token); 9851592Srgrimes } 9861592Srgrimes break; 9871592Srgrimes 9881592Srgrimes case SITECMD: 9891592Srgrimes if (cbuf[cpos] == ' ') { 9901592Srgrimes cpos++; 9911592Srgrimes return (SP); 9921592Srgrimes } 9931592Srgrimes cp = &cbuf[cpos]; 9941592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 9951592Srgrimes cpos = cp2 - cbuf; 9961592Srgrimes c = cbuf[cpos]; 9971592Srgrimes cbuf[cpos] = '\0'; 9981592Srgrimes upper(cp); 9991592Srgrimes p = lookup(sitetab, cp); 10001592Srgrimes cbuf[cpos] = c; 10013777Spst if (guest == 0 && p != 0) { 10021592Srgrimes if (p->implemented == 0) { 10031592Srgrimes state = CMD; 10041592Srgrimes nack(p->name); 10051592Srgrimes longjmp(errcatch,0); 10061592Srgrimes /* NOTREACHED */ 10071592Srgrimes } 10081592Srgrimes state = p->state; 10091592Srgrimes yylval.s = p->name; 10101592Srgrimes return (p->token); 10111592Srgrimes } 10121592Srgrimes state = CMD; 10131592Srgrimes break; 10141592Srgrimes 10151592Srgrimes case OSTR: 10161592Srgrimes if (cbuf[cpos] == '\n') { 10171592Srgrimes state = CMD; 10181592Srgrimes return (CRLF); 10191592Srgrimes } 10201592Srgrimes /* FALLTHROUGH */ 10211592Srgrimes 10221592Srgrimes case STR1: 10231592Srgrimes case ZSTR1: 10241592Srgrimes dostr1: 10251592Srgrimes if (cbuf[cpos] == ' ') { 10261592Srgrimes cpos++; 10271592Srgrimes state = state == OSTR ? STR2 : ++state; 10281592Srgrimes return (SP); 10291592Srgrimes } 10301592Srgrimes break; 10311592Srgrimes 10321592Srgrimes case ZSTR2: 10331592Srgrimes if (cbuf[cpos] == '\n') { 10341592Srgrimes state = CMD; 10351592Srgrimes return (CRLF); 10361592Srgrimes } 10371592Srgrimes /* FALLTHROUGH */ 10381592Srgrimes 10391592Srgrimes case STR2: 10401592Srgrimes cp = &cbuf[cpos]; 10411592Srgrimes n = strlen(cp); 10421592Srgrimes cpos += n - 1; 10431592Srgrimes /* 10441592Srgrimes * Make sure the string is nonempty and \n terminated. 10451592Srgrimes */ 10461592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 10471592Srgrimes cbuf[cpos] = '\0'; 10481592Srgrimes yylval.s = copy(cp); 10491592Srgrimes cbuf[cpos] = '\n'; 10501592Srgrimes state = ARGS; 10511592Srgrimes return (STRING); 10521592Srgrimes } 10531592Srgrimes break; 10541592Srgrimes 10551592Srgrimes case NSTR: 10561592Srgrimes if (cbuf[cpos] == ' ') { 10571592Srgrimes cpos++; 10581592Srgrimes return (SP); 10591592Srgrimes } 10601592Srgrimes if (isdigit(cbuf[cpos])) { 10611592Srgrimes cp = &cbuf[cpos]; 10621592Srgrimes while (isdigit(cbuf[++cpos])) 10631592Srgrimes ; 10641592Srgrimes c = cbuf[cpos]; 10651592Srgrimes cbuf[cpos] = '\0'; 10661592Srgrimes yylval.i = atoi(cp); 10671592Srgrimes cbuf[cpos] = c; 10681592Srgrimes state = STR1; 10691592Srgrimes return (NUMBER); 10701592Srgrimes } 10711592Srgrimes state = STR1; 10721592Srgrimes goto dostr1; 10731592Srgrimes 10741592Srgrimes case ARGS: 10751592Srgrimes if (isdigit(cbuf[cpos])) { 10761592Srgrimes cp = &cbuf[cpos]; 10771592Srgrimes while (isdigit(cbuf[++cpos])) 10781592Srgrimes ; 10791592Srgrimes c = cbuf[cpos]; 10801592Srgrimes cbuf[cpos] = '\0'; 10811592Srgrimes yylval.i = atoi(cp); 10821592Srgrimes cbuf[cpos] = c; 10831592Srgrimes return (NUMBER); 10841592Srgrimes } 10851592Srgrimes switch (cbuf[cpos++]) { 10861592Srgrimes 10871592Srgrimes case '\n': 10881592Srgrimes state = CMD; 10891592Srgrimes return (CRLF); 10901592Srgrimes 10911592Srgrimes case ' ': 10921592Srgrimes return (SP); 10931592Srgrimes 10941592Srgrimes case ',': 10951592Srgrimes return (COMMA); 10961592Srgrimes 10971592Srgrimes case 'A': 10981592Srgrimes case 'a': 10991592Srgrimes return (A); 11001592Srgrimes 11011592Srgrimes case 'B': 11021592Srgrimes case 'b': 11031592Srgrimes return (B); 11041592Srgrimes 11051592Srgrimes case 'C': 11061592Srgrimes case 'c': 11071592Srgrimes return (C); 11081592Srgrimes 11091592Srgrimes case 'E': 11101592Srgrimes case 'e': 11111592Srgrimes return (E); 11121592Srgrimes 11131592Srgrimes case 'F': 11141592Srgrimes case 'f': 11151592Srgrimes return (F); 11161592Srgrimes 11171592Srgrimes case 'I': 11181592Srgrimes case 'i': 11191592Srgrimes return (I); 11201592Srgrimes 11211592Srgrimes case 'L': 11221592Srgrimes case 'l': 11231592Srgrimes return (L); 11241592Srgrimes 11251592Srgrimes case 'N': 11261592Srgrimes case 'n': 11271592Srgrimes return (N); 11281592Srgrimes 11291592Srgrimes case 'P': 11301592Srgrimes case 'p': 11311592Srgrimes return (P); 11321592Srgrimes 11331592Srgrimes case 'R': 11341592Srgrimes case 'r': 11351592Srgrimes return (R); 11361592Srgrimes 11371592Srgrimes case 'S': 11381592Srgrimes case 's': 11391592Srgrimes return (S); 11401592Srgrimes 11411592Srgrimes case 'T': 11421592Srgrimes case 't': 11431592Srgrimes return (T); 11441592Srgrimes 11451592Srgrimes } 11461592Srgrimes break; 11471592Srgrimes 11481592Srgrimes default: 11491592Srgrimes fatal("Unknown state in scanner."); 11501592Srgrimes } 11511592Srgrimes yyerror((char *) 0); 11521592Srgrimes state = CMD; 11531592Srgrimes longjmp(errcatch,0); 11541592Srgrimes } 11551592Srgrimes} 11561592Srgrimes 11571592Srgrimesvoid 11581592Srgrimesupper(s) 11591592Srgrimes char *s; 11601592Srgrimes{ 11611592Srgrimes while (*s != '\0') { 11621592Srgrimes if (islower(*s)) 11631592Srgrimes *s = toupper(*s); 11641592Srgrimes s++; 11651592Srgrimes } 11661592Srgrimes} 11671592Srgrimes 11681592Srgrimesstatic char * 11691592Srgrimescopy(s) 11701592Srgrimes char *s; 11711592Srgrimes{ 11721592Srgrimes char *p; 11731592Srgrimes 11741592Srgrimes p = malloc((unsigned) strlen(s) + 1); 11751592Srgrimes if (p == NULL) 11761592Srgrimes fatal("Ran out of memory."); 11771592Srgrimes (void) strcpy(p, s); 11781592Srgrimes return (p); 11791592Srgrimes} 11801592Srgrimes 11811592Srgrimesstatic void 11821592Srgrimeshelp(ctab, s) 11831592Srgrimes struct tab *ctab; 11841592Srgrimes char *s; 11851592Srgrimes{ 11861592Srgrimes struct tab *c; 11871592Srgrimes int width, NCMDS; 11881592Srgrimes char *type; 11891592Srgrimes 11901592Srgrimes if (ctab == sitetab) 11911592Srgrimes type = "SITE "; 11921592Srgrimes else 11931592Srgrimes type = ""; 11941592Srgrimes width = 0, NCMDS = 0; 11951592Srgrimes for (c = ctab; c->name != NULL; c++) { 11961592Srgrimes int len = strlen(c->name); 11971592Srgrimes 11981592Srgrimes if (len > width) 11991592Srgrimes width = len; 12001592Srgrimes NCMDS++; 12011592Srgrimes } 12021592Srgrimes width = (width + 8) &~ 7; 12031592Srgrimes if (s == 0) { 12041592Srgrimes int i, j, w; 12051592Srgrimes int columns, lines; 12061592Srgrimes 12071592Srgrimes lreply(214, "The following %scommands are recognized %s.", 12081592Srgrimes type, "(* =>'s unimplemented)"); 12091592Srgrimes columns = 76 / width; 12101592Srgrimes if (columns == 0) 12111592Srgrimes columns = 1; 12121592Srgrimes lines = (NCMDS + columns - 1) / columns; 12131592Srgrimes for (i = 0; i < lines; i++) { 12141592Srgrimes printf(" "); 12151592Srgrimes for (j = 0; j < columns; j++) { 12161592Srgrimes c = ctab + j * lines + i; 12171592Srgrimes printf("%s%c", c->name, 12181592Srgrimes c->implemented ? ' ' : '*'); 12191592Srgrimes if (c + lines >= &ctab[NCMDS]) 12201592Srgrimes break; 12211592Srgrimes w = strlen(c->name) + 1; 12221592Srgrimes while (w < width) { 12231592Srgrimes putchar(' '); 12241592Srgrimes w++; 12251592Srgrimes } 12261592Srgrimes } 12271592Srgrimes printf("\r\n"); 12281592Srgrimes } 12291592Srgrimes (void) fflush(stdout); 12301592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 12311592Srgrimes return; 12321592Srgrimes } 12331592Srgrimes upper(s); 12341592Srgrimes c = lookup(ctab, s); 12351592Srgrimes if (c == (struct tab *)0) { 12361592Srgrimes reply(502, "Unknown command %s.", s); 12371592Srgrimes return; 12381592Srgrimes } 12391592Srgrimes if (c->implemented) 12401592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 12411592Srgrimes else 12421592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 12431592Srgrimes c->name, c->help); 12441592Srgrimes} 12451592Srgrimes 12461592Srgrimesstatic void 12471592Srgrimessizecmd(filename) 12481592Srgrimes char *filename; 12491592Srgrimes{ 12501592Srgrimes switch (type) { 12511592Srgrimes case TYPE_L: 12521592Srgrimes case TYPE_I: { 12531592Srgrimes struct stat stbuf; 12541592Srgrimes if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 12551592Srgrimes reply(550, "%s: not a plain file.", filename); 12561592Srgrimes else 12571592Srgrimes reply(213, "%qu", stbuf.st_size); 12581592Srgrimes break; } 12591592Srgrimes case TYPE_A: { 12601592Srgrimes FILE *fin; 12611592Srgrimes int c; 12621592Srgrimes off_t count; 12631592Srgrimes struct stat stbuf; 12641592Srgrimes fin = fopen(filename, "r"); 12651592Srgrimes if (fin == NULL) { 12661592Srgrimes perror_reply(550, filename); 12671592Srgrimes return; 12681592Srgrimes } 12691592Srgrimes if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 12701592Srgrimes reply(550, "%s: not a plain file.", filename); 12711592Srgrimes (void) fclose(fin); 12721592Srgrimes return; 12731592Srgrimes } 12741592Srgrimes 12751592Srgrimes count = 0; 12761592Srgrimes while((c=getc(fin)) != EOF) { 12771592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 12781592Srgrimes count++; 12791592Srgrimes count++; 12801592Srgrimes } 12811592Srgrimes (void) fclose(fin); 12821592Srgrimes 12831592Srgrimes reply(213, "%qd", count); 12841592Srgrimes break; } 12851592Srgrimes default: 12861592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 12871592Srgrimes } 12881592Srgrimes} 1289