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