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