ftpcmd.y revision 1.30
11556Srgrimes/* $OpenBSD: ftpcmd.y,v 1.30 2002/01/08 01:55:27 millert Exp $ */ 21556Srgrimes/* $NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp $ */ 31556Srgrimes 41556Srgrimes/* 51556Srgrimes * Copyright (c) 1985, 1988, 1993, 1994 61556Srgrimes * The Regents of the University of California. All rights reserved. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 353044Sdg * 3620742Ssteve * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 371556Srgrimes */ 381556Srgrimes 391556Srgrimes/* 4020425Ssteve * Grammar for FTP commands. 411556Srgrimes * See RFC 959. 421556Srgrimes */ 4317987Speter 4417987Speter%{ 4517987Speter 4617987Speter#ifndef lint 471556Srgrimes#if 0 481556Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 491556Srgrimes#else 501556Srgrimesstatic char rcsid[] = "$OpenBSD: ftpcmd.y,v 1.30 2002/01/08 01:55:27 millert Exp $"; 511556Srgrimes#endif 521556Srgrimes#endif /* not lint */ 531556Srgrimes 541556Srgrimes#include <sys/param.h> 551556Srgrimes#include <sys/socket.h> 561556Srgrimes#include <sys/stat.h> 571556Srgrimes 581556Srgrimes#include <netinet/in.h> 591556Srgrimes#include <arpa/ftp.h> 601556Srgrimes 6117987Speter#include <ctype.h> 6217987Speter#include <errno.h> 6317987Speter#include <glob.h> 641556Srgrimes#include <pwd.h> 651556Srgrimes#include <signal.h> 661556Srgrimes#include <tzfile.h> 671556Srgrimes#include <stdio.h> 681556Srgrimes#include <stdlib.h> 691556Srgrimes#include <string.h> 701556Srgrimes#include <syslog.h> 711556Srgrimes#include <time.h> 721556Srgrimes#include <unistd.h> 731556Srgrimes#include <netdb.h> 7417987Speter 7517987Speter#include "extern.h" 7617987Speter 7720425Ssteveextern union sockunion data_dest; 781556Srgrimesextern int logged_in; 791556Srgrimesextern struct passwd *pw; 801556Srgrimesextern int guest; 811556Srgrimesextern int logging; 821556Srgrimesextern int type; 831556Srgrimesextern int form; 841556Srgrimesextern int debug; 851556Srgrimesextern int timeout; 8617987Speterextern int maxtimeout; 871556Srgrimesextern int pdata; 8817987Speterextern char hostname[], remotehost[]; 891556Srgrimesextern char proctitle[]; 901556Srgrimesextern int usedefault; 911556Srgrimesextern int transflag; 921556Srgrimesextern char tmpline[]; 931556Srgrimesextern int portcheck; 941556Srgrimesextern union sockunion his_addr; 951556Srgrimesextern int umaskchange; 9619240Ssteve 971556Srgrimesoff_t restart_point; 981556Srgrimes 991556Srgrimesstatic int cmd_type; 1001556Srgrimesstatic int cmd_form; 1011556Srgrimesstatic int cmd_bytesz; 1021556Srgrimesstatic int state; 1031556Srgrimeschar cbuf[512]; 1041556Srgrimeschar *fromname; 1051556Srgrimes 1061556Srgrimes%} 1071556Srgrimes 1081556Srgrimes%union { 1091556Srgrimes int i; 1101556Srgrimes char *s; 1111556Srgrimes} 11220425Ssteve 11320742Ssteve%token 11411111Sjoerg A B C E F I 11520742Ssteve L N P R S T 1161556Srgrimes ALL 11720742Ssteve 1181556Srgrimes SP CRLF COMMA 1191556Srgrimes 1201556Srgrimes USER PASS ACCT REIN QUIT PORT 1211556Srgrimes PASV TYPE STRU MODE RETR STOR 1221556Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1231556Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1241556Srgrimes ABOR DELE CWD LIST NLST SITE 1251556Srgrimes STAT HELP NOOP MKD RMD PWD 1261556Srgrimes CDUP STOU SMNT SYST SIZE MDTM 12717987Speter 12817987Speter LPRT LPSV EPRT EPSV 12917987Speter 1301556Srgrimes UMASK IDLE CHMOD 13117987Speter 1321556Srgrimes LEXERR 13317987Speter 1341556Srgrimes%token <s> STRING 1351556Srgrimes%token <s> ALL 1361556Srgrimes%token <i> NUMBER 1371556Srgrimes 1381556Srgrimes%type <i> check_login check_login_epsvall octal_number byte_size 1391556Srgrimes%type <i> struct_code mode_code type_code form_code 1401556Srgrimes%type <s> pathstring pathname password username 1411556Srgrimes%type <i> host_port host_long_port4 host_long_port6 1421556Srgrimes 14320425Ssteve%start cmd_list 14417987Speter 14517987Speter%% 1461556Srgrimes 1471556Srgrimescmd_list 1481556Srgrimes : /* empty */ 1491556Srgrimes | cmd_list cmd 1501556Srgrimes { 1511556Srgrimes if (fromname) { 1521556Srgrimes free(fromname); 1531556Srgrimes fromname = NULL; 1541556Srgrimes } 1551556Srgrimes restart_point = (off_t) 0; 15617987Speter } 1571556Srgrimes | cmd_list rcmd 1581556Srgrimes ; 1591556Srgrimes 1601556Srgrimescmd 1611556Srgrimes : USER SP username CRLF 1621556Srgrimes { 16317987Speter user($3); 1641556Srgrimes free($3); 1651556Srgrimes } 1661556Srgrimes | PASS SP password CRLF 1671556Srgrimes { 1681556Srgrimes pass($3); 1691556Srgrimes memset($3, 0, strlen($3)); 1701556Srgrimes free($3); 1711556Srgrimes } 1721556Srgrimes | PORT check_login_epsvall SP host_port CRLF 1731556Srgrimes { 1741556Srgrimes if ($2) { 1751556Srgrimes if ($4) { 1761556Srgrimes usedefault = 1; 1771556Srgrimes reply(500, 1781556Srgrimes "Illegal PORT rejected (range errors)."); 1791556Srgrimes } else if (portcheck && 1801556Srgrimes ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) { 1811556Srgrimes usedefault = 1; 1821556Srgrimes reply(500, 1831556Srgrimes "Illegal PORT rejected (reserved port)."); 1841556Srgrimes } else if (portcheck && 1851556Srgrimes memcmp(&data_dest.su_sin.sin_addr, 1861556Srgrimes &his_addr.su_sin.sin_addr, 1871556Srgrimes sizeof data_dest.su_sin.sin_addr)) { 1881556Srgrimes usedefault = 1; 1891556Srgrimes reply(500, 1901556Srgrimes "Illegal PORT rejected (address wrong)."); 19119240Ssteve } else { 19219240Ssteve usedefault = 0; 19319240Ssteve if (pdata >= 0) { 19419240Ssteve (void) close(pdata); 1951556Srgrimes pdata = -1; 1961556Srgrimes } 1971556Srgrimes reply(200, "PORT command successful."); 1981556Srgrimes } 1991556Srgrimes } 2001556Srgrimes } 2011556Srgrimes | LPRT check_login_epsvall SP host_long_port4 CRLF 2021556Srgrimes { 2031556Srgrimes if ($2) { 2041556Srgrimes /* reject invalid host_long_port4 */ 2051556Srgrimes if ($4) { 2061556Srgrimes reply(500, 2071556Srgrimes "Illegal LPRT command rejected"); 2081556Srgrimes usedefault = 1; 2091556Srgrimes } else { 2101556Srgrimes usedefault = 0; 2111556Srgrimes if (pdata >= 0) { 2121556Srgrimes (void) close(pdata); 2131556Srgrimes pdata = -1; 2141556Srgrimes } 2151556Srgrimes reply(200, "LPRT command successful."); 21619240Ssteve } 21719240Ssteve } 21819240Ssteve } 21919240Ssteve 2201556Srgrimes | LPRT check_login_epsvall SP host_long_port6 CRLF 2211556Srgrimes { 2221556Srgrimes if ($2) { 2231556Srgrimes /* reject invalid host_long_port6 */ 2241556Srgrimes if ($4) { 2251556Srgrimes reply(500, 2261556Srgrimes "Illegal LPRT command rejected"); 2278855Srgrimes usedefault = 1; 2281556Srgrimes } else { 2291556Srgrimes usedefault = 0; 2301556Srgrimes if (pdata >= 0) { 2311556Srgrimes (void) close(pdata); 2321556Srgrimes pdata = -1; 2331556Srgrimes } 2341556Srgrimes reply(200, "LPRT command successful."); 2351556Srgrimes } 2361556Srgrimes } 2371556Srgrimes } 2381556Srgrimes 2391556Srgrimes | EPRT check_login_epsvall SP STRING CRLF 2401556Srgrimes { 2411556Srgrimes if ($2) 2421556Srgrimes extended_port($4); 2431556Srgrimes free($4); 2441556Srgrimes } 2451556Srgrimes 2461556Srgrimes | PASV check_login_epsvall CRLF 2471556Srgrimes { 2481556Srgrimes if ($2) 2491556Srgrimes passive(); 2501556Srgrimes } 2511556Srgrimes | LPSV check_login_epsvall CRLF 2521556Srgrimes { 2531556Srgrimes if ($2) 2541556Srgrimes long_passive("LPSV", PF_UNSPEC); 2551556Srgrimes } 2561556Srgrimes | EPSV check_login SP NUMBER CRLF 2571556Srgrimes { 2581556Srgrimes if ($2) 2591556Srgrimes long_passive("EPSV", epsvproto2af($4)); 2601556Srgrimes } 2611556Srgrimes | EPSV check_login SP ALL CRLF 2621556Srgrimes { 2631556Srgrimes if ($2) { 2641556Srgrimes reply(200, "EPSV ALL command successful."); 2651556Srgrimes epsvall++; 2661556Srgrimes } 2671556Srgrimes } 2681556Srgrimes | EPSV check_login CRLF 2691556Srgrimes { 2701556Srgrimes if ($2) 2711556Srgrimes long_passive("EPSV", PF_UNSPEC); 2721556Srgrimes } 2731556Srgrimes | TYPE check_login SP type_code CRLF 2741556Srgrimes { 2751556Srgrimes if ($2) { 2761556Srgrimes switch (cmd_type) { 2771556Srgrimes 2781556Srgrimes case TYPE_A: 2791556Srgrimes if (cmd_form == FORM_N) { 2801556Srgrimes reply(200, "Type set to A."); 2811556Srgrimes type = cmd_type; 2821556Srgrimes form = cmd_form; 2831556Srgrimes } else 2841556Srgrimes reply(504, "Form must be N."); 2851556Srgrimes break; 2861556Srgrimes 2871556Srgrimes case TYPE_E: 2881556Srgrimes reply(504, "Type E not implemented."); 28920742Ssteve break; 2901556Srgrimes 2911556Srgrimes case TYPE_I: 2921556Srgrimes reply(200, "Type set to I."); 2931556Srgrimes type = cmd_type; 2941556Srgrimes break; 2951556Srgrimes 2961556Srgrimes case TYPE_L: 2971556Srgrimes if (cmd_bytesz == 8) { 2981556Srgrimes reply(200, 2991556Srgrimes "Type set to L (byte size 8)."); 3001556Srgrimes type = cmd_type; 3011556Srgrimes } else 3021556Srgrimes reply(504, "Byte size must be 8."); 3031556Srgrimes 3041556Srgrimes } 3051556Srgrimes } 3061556Srgrimes } 3071556Srgrimes | STRU check_login SP struct_code CRLF 3081556Srgrimes { 3091556Srgrimes if ($2) { 3101556Srgrimes switch ($4) { 3111556Srgrimes 3121556Srgrimes case STRU_F: 3131556Srgrimes reply(200, "STRU F ok."); 3141556Srgrimes break; 3151556Srgrimes 31617987Speter default: 31717987Speter reply(504, "Unimplemented STRU type."); 31817987Speter } 31920425Ssteve } 32017987Speter } 3211556Srgrimes | MODE check_login SP mode_code CRLF 3221556Srgrimes { 3231556Srgrimes if ($2) { 3241556Srgrimes switch ($4) { 3251556Srgrimes 3261556Srgrimes case MODE_S: 3271556Srgrimes reply(200, "MODE S ok."); 3281556Srgrimes break; 3291556Srgrimes 3301556Srgrimes default: 3311556Srgrimes reply(502, "Unimplemented MODE type."); 3321556Srgrimes } 3331556Srgrimes } 3341556Srgrimes } 3351556Srgrimes | ALLO check_login SP NUMBER CRLF 3361556Srgrimes { 3371556Srgrimes if ($2) { 3381556Srgrimes reply(202, "ALLO command ignored."); 3391556Srgrimes } 3401556Srgrimes } 3411556Srgrimes | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 3421556Srgrimes { 3431556Srgrimes if ($2) { 3441556Srgrimes reply(202, "ALLO command ignored."); 3451556Srgrimes } 3461556Srgrimes } 3471556Srgrimes | RETR check_login SP pathname CRLF 34817987Speter { 34917987Speter if ($2 && $4 != NULL) 35017987Speter retrieve(NULL, $4); 35120425Ssteve if ($4 != NULL) 35217987Speter free($4); 3531556Srgrimes } 3541556Srgrimes | STOR check_login SP pathname CRLF 3551556Srgrimes { 3561556Srgrimes if ($2 && $4 != NULL) 3571556Srgrimes store($4, "w", 0); 3581556Srgrimes if ($4 != NULL) 3591556Srgrimes free($4); 3601556Srgrimes } 3611556Srgrimes | APPE check_login SP pathname CRLF 3621556Srgrimes { 3631556Srgrimes if ($2 && $4 != NULL) 3641556Srgrimes store($4, "a", 0); 3651556Srgrimes if ($4 != NULL) 36620425Ssteve free($4); 36720425Ssteve } 36820425Ssteve | NLST check_login CRLF 36920425Ssteve { 37020425Ssteve if ($2) 37120425Ssteve send_file_list("."); 37220425Ssteve } 37320425Ssteve | NLST check_login SP STRING CRLF 37420425Ssteve { 37520425Ssteve if ($2 && $4 != NULL) 3761556Srgrimes send_file_list($4); 3771556Srgrimes free($4); 3781556Srgrimes } 3791556Srgrimes | LIST check_login CRLF 3801556Srgrimes { 3811556Srgrimes if ($2) 3821556Srgrimes retrieve("/bin/ls -lgA", ""); 38317987Speter } 38417987Speter | LIST check_login SP pathname CRLF 38517987Speter { 38620425Ssteve if ($2 && $4 != NULL) 38717987Speter retrieve("/bin/ls -lgA %s", $4); 38820425Ssteve if ($4 != NULL) 38920425Ssteve free($4); 39020425Ssteve } 39120425Ssteve | STAT check_login SP pathname CRLF 39220425Ssteve { 39320425Ssteve if ($2 && $4 != NULL) 39420425Ssteve statfilecmd($4); 39520425Ssteve if ($4 != NULL) 39620425Ssteve free($4); 39720425Ssteve } 39820425Ssteve | STAT check_login CRLF 39920425Ssteve { 40020425Ssteve if ($2) 40120425Ssteve statcmd(); 40220425Ssteve } 40320425Ssteve | DELE check_login SP pathname CRLF 40420425Ssteve { 40520425Ssteve if ($2 && $4 != NULL) 40620425Ssteve delete($4); 40720425Ssteve if ($4 != NULL) 40820425Ssteve free($4); 40920425Ssteve } 41020425Ssteve | RNTO check_login SP pathname CRLF 41120425Ssteve { 41220425Ssteve if ($2) { 41320425Ssteve if (fromname) { 41420425Ssteve renamecmd(fromname, $4); 4151556Srgrimes free(fromname); 41620425Ssteve fromname = NULL; 41720425Ssteve } else { 41820425Ssteve reply(503, 41920425Ssteve "Bad sequence of commands."); 4201556Srgrimes } 4211556Srgrimes } 42220425Ssteve free($4); 42320425Ssteve } 42420425Ssteve | ABOR check_login CRLF 42520425Ssteve { 42620425Ssteve if ($2) 4271556Srgrimes reply(225, "ABOR command successful."); 4281556Srgrimes } 42920742Ssteve | CWD check_login CRLF 43020425Ssteve { 43120742Ssteve if ($2) 43220425Ssteve cwd(pw->pw_dir); 43320425Ssteve } 4341556Srgrimes | CWD check_login SP pathname CRLF 43520425Ssteve { 4361556Srgrimes if ($2 && $4 != NULL) 4371556Srgrimes cwd($4); 4381556Srgrimes if ($4 != NULL) 43920425Ssteve free($4); 4401556Srgrimes } 44120425Ssteve | HELP CRLF 4421556Srgrimes { 44320425Ssteve help(cmdtab, NULL); 44420425Ssteve } 44520425Ssteve | HELP SP STRING CRLF 44620425Ssteve { 44720425Ssteve char *cp = $3; 44820425Ssteve 44920425Ssteve if (strncasecmp(cp, "SITE", 4) == 0) { 45020425Ssteve cp = $3 + 4; 45120425Ssteve if (*cp == ' ') 4521556Srgrimes cp++; 45320425Ssteve if (*cp) 4541556Srgrimes help(sitetab, cp); 4551556Srgrimes else 4561556Srgrimes help(sitetab, NULL); 4571556Srgrimes } else 45820425Ssteve help(cmdtab, $3); 4591556Srgrimes free ($3); 46020425Ssteve } 46120425Ssteve | NOOP CRLF 46220425Ssteve { 46320425Ssteve reply(200, "NOOP command successful."); 46420425Ssteve } 46520425Ssteve | MKD check_login SP pathname CRLF 46620425Ssteve { 46720425Ssteve if ($2 && $4 != NULL) 46820425Ssteve makedir($4); 46920425Ssteve if ($4 != NULL) 47020425Ssteve free($4); 47120425Ssteve } 47220425Ssteve | RMD check_login SP pathname CRLF 4731556Srgrimes { 47420425Ssteve if ($2 && $4 != NULL) 47520425Ssteve removedir($4); 47620425Ssteve if ($4 != NULL) 47720425Ssteve free($4); 4781556Srgrimes } 4791556Srgrimes | PWD check_login CRLF 48020425Ssteve { 48120425Ssteve if ($2) 48220425Ssteve pwd(); 48320425Ssteve } 48420425Ssteve | CDUP check_login CRLF 48520425Ssteve { 48620425Ssteve if ($2) 48720425Ssteve cwd(".."); 48820425Ssteve } 4891556Srgrimes | SITE SP HELP CRLF 49020425Ssteve { 49120425Ssteve help(sitetab, NULL); 49220425Ssteve } 4931556Srgrimes | SITE SP HELP SP STRING CRLF 4941556Srgrimes { 49520425Ssteve help(sitetab, $5); 49620425Ssteve free ($5); 49720425Ssteve } 49820425Ssteve | SITE SP UMASK check_login CRLF 49920425Ssteve { 50020425Ssteve int oldmask; 50120425Ssteve 50220425Ssteve if ($4) { 5031556Srgrimes oldmask = umask(0); 5041556Srgrimes (void) umask(oldmask); 5051556Srgrimes reply(200, "Current UMASK is %03o", oldmask); 5061556Srgrimes } 5071556Srgrimes } 5081556Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 5091556Srgrimes { 5101556Srgrimes int oldmask; 5111556Srgrimes 5121556Srgrimes if ($4) { 5131556Srgrimes if (($6 == -1) || ($6 > 0777)) { 5141556Srgrimes reply(501, "Bad UMASK value"); 5151556Srgrimes } else if (!umaskchange) { 5161556Srgrimes reply(550, 5171556Srgrimes "No permission to change umask."); 5181556Srgrimes } else { 5191556Srgrimes oldmask = umask($6); 5201556Srgrimes reply(200, 5211556Srgrimes "UMASK set to %03o (was %03o)", 5221556Srgrimes $6, oldmask); 5231556Srgrimes } 5241556Srgrimes } 5251556Srgrimes } 5261556Srgrimes | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 5271556Srgrimes { 5281556Srgrimes if ($4 && ($8 != NULL)) { 5291556Srgrimes if ($6 > 0777) 5301556Srgrimes reply(501, 5311556Srgrimes "CHMOD: Mode value must be between " 5321556Srgrimes "0 and 0777"); 5331556Srgrimes else if (!umaskchange) 5341556Srgrimes reply(550, 5351556Srgrimes "No permission to change mode of %s.", 5361556Srgrimes $8); 5371556Srgrimes else if (chmod($8, $6) < 0) 5381556Srgrimes perror_reply(550, $8); 5391556Srgrimes else 5401556Srgrimes reply(200, 5411556Srgrimes "CHMOD command successful."); 5421556Srgrimes } 5431556Srgrimes if ($8 != NULL) 5441556Srgrimes free($8); 5451556Srgrimes } 5461556Srgrimes | SITE SP check_login IDLE CRLF 547 { 548 if ($3) 549 reply(200, 550 "Current IDLE time limit is %d seconds; max %d", 551 timeout, maxtimeout); 552 } 553 | SITE SP check_login IDLE SP NUMBER CRLF 554 { 555 if ($3) { 556 if ($6 < 30 || $6 > maxtimeout) { 557 reply(501, 558 "Maximum IDLE time must be between " 559 "30 and %d seconds", 560 maxtimeout); 561 } else { 562 timeout = $6; 563 (void) alarm((unsigned) timeout); 564 reply(200, 565 "Maximum IDLE time set to %d seconds", 566 timeout); 567 } 568 } 569 } 570 | STOU check_login SP pathname CRLF 571 { 572 if ($2 && $4 != NULL) 573 store($4, "w", 1); 574 if ($4 != NULL) 575 free($4); 576 } 577 | SYST check_login CRLF 578 { 579 if ($2) 580#ifdef unix 581#ifdef BSD 582 reply(215, "UNIX Type: L%d Version: BSD-%d", 583 NBBY, BSD); 584#else /* BSD */ 585 reply(215, "UNIX Type: L%d", NBBY); 586#endif /* BSD */ 587#else /* unix */ 588 reply(215, "UNKNOWN Type: L%d", NBBY); 589#endif /* unix */ 590 } 591 592 /* 593 * SIZE is not in RFC959, but Postel has blessed it and 594 * it will be in the updated RFC. 595 * 596 * Return size of file in a format suitable for 597 * using with RESTART (we just count bytes). 598 */ 599 | SIZE check_login SP pathname CRLF 600 { 601 if ($2 && $4 != NULL) 602 sizecmd($4); 603 if ($4 != NULL) 604 free($4); 605 } 606 607 /* 608 * MDTM is not in RFC959, but Postel has blessed it and 609 * it will be in the updated RFC. 610 * 611 * Return modification time of file as an ISO 3307 612 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 613 * where xxx is the fractional second (of any precision, 614 * not necessarily 3 digits) 615 */ 616 | MDTM check_login SP pathname CRLF 617 { 618 if ($2 && $4 != NULL) { 619 struct stat stbuf; 620 if (stat($4, &stbuf) < 0) 621 reply(550, "%s: %s", 622 $4, strerror(errno)); 623 else if (!S_ISREG(stbuf.st_mode)) { 624 reply(550, "%s: not a plain file.", $4); 625 } else { 626 struct tm *t; 627 t = gmtime(&stbuf.st_mtime); 628 reply(213, 629 "%04d%02d%02d%02d%02d%02d", 630 TM_YEAR_BASE + t->tm_year, 631 t->tm_mon+1, t->tm_mday, 632 t->tm_hour, t->tm_min, t->tm_sec); 633 } 634 } 635 if ($4 != NULL) 636 free($4); 637 } 638 | QUIT CRLF 639 { 640 reply(221, "Goodbye."); 641 dologout(0); 642 } 643 | error 644 { 645 yyclearin; /* discard lookahead data */ 646 yyerrok; /* clear error condition */ 647 state = 0; /* reset lexer state */ 648 } 649 ; 650rcmd 651 : RNFR check_login SP pathname CRLF 652 { 653 restart_point = (off_t) 0; 654 if ($2 && $4) { 655 if (fromname) 656 free(fromname); 657 fromname = renamefrom($4); 658 if (fromname == NULL) 659 free($4); 660 } else if ($4) { 661 free ($4); 662 } 663 } 664 665 | REST check_login SP byte_size CRLF 666 { 667 if ($2) { 668 if (fromname) { 669 free(fromname); 670 fromname = NULL; 671 } 672 restart_point = $4; /* XXX $4 is only "int" */ 673 reply(350, "Restarting at %qd. %s", restart_point, 674 "Send STORE or RETRIEVE to initiate transfer."); 675 } 676 } 677 ; 678 679username 680 : STRING 681 ; 682 683password 684 : /* empty */ 685 { 686 $$ = (char *)calloc(1, sizeof(char)); 687 } 688 | STRING 689 ; 690 691byte_size 692 : NUMBER 693 ; 694 695host_port 696 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 697 NUMBER COMMA NUMBER 698 { 699 char *a, *p; 700 701 if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 || 702 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 703 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { 704 $$ = 1; 705 } else { 706 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 707 data_dest.su_sin.sin_family = AF_INET; 708 p = (char *)&data_dest.su_sin.sin_port; 709 p[0] = $9; p[1] = $11; 710 a = (char *)&data_dest.su_sin.sin_addr; 711 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 712 $$ = 0; 713 } 714 } 715 ; 716 717host_long_port4 718 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 719 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 720 NUMBER 721 { 722 char *a, *p; 723 724 /* reject invalid LPRT command */ 725 if ($1 != 4 || $3 != 4 726 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 727 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 728 || $13 != 2 729 || $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) { 730 $$ = 1; 731 } else { 732 data_dest.su_sin.sin_len = 733 sizeof(struct sockaddr_in); 734 data_dest.su_family = AF_INET; 735 p = (char *)&data_dest.su_port; 736 p[0] = $15; p[1] = $17; 737 a = (char *)&data_dest.su_sin.sin_addr; 738 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 739 $$ = 0; 740 } 741 } 742 ; 743 744host_long_port6 745 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 746 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 747 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 748 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 749 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 750 NUMBER 751 { 752 char *a, *p; 753 754 /* reject invalid LPRT command */ 755 if ($1 != 6 || $3 != 16 756 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 757 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 758 || $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 759 || $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 760 || $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 761 || $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 762 || $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 763 || $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 764 || $37 != 2 765 || $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) { 766 $$ = 1; 767 } else { 768 data_dest.su_sin6.sin6_len = 769 sizeof(struct sockaddr_in6); 770 data_dest.su_family = AF_INET6; 771 p = (char *)&data_dest.su_port; 772 p[0] = $39; p[1] = $41; 773 a = (char *)&data_dest.su_sin6.sin6_addr; 774 a[0] = $5; a[1] = $7; 775 a[2] = $9; a[3] = $11; 776 a[4] = $13; a[5] = $15; 777 a[6] = $17; a[7] = $19; 778 a[8] = $21; a[9] = $23; 779 a[10] = $25; a[11] = $27; 780 a[12] = $29; a[13] = $31; 781 a[14] = $33; a[15] = $35; 782 if (his_addr.su_family == AF_INET6) { 783 /* XXX more sanity checks! */ 784 data_dest.su_sin6.sin6_scope_id = 785 his_addr.su_sin6.sin6_scope_id; 786 } 787 788 $$ = 0; 789 } 790 } 791 ; 792 793form_code 794 : N 795 { 796 $$ = FORM_N; 797 } 798 | T 799 { 800 $$ = FORM_T; 801 } 802 | C 803 { 804 $$ = FORM_C; 805 } 806 ; 807 808type_code 809 : A 810 { 811 cmd_type = TYPE_A; 812 cmd_form = FORM_N; 813 } 814 | A SP form_code 815 { 816 cmd_type = TYPE_A; 817 cmd_form = $3; 818 } 819 | E 820 { 821 cmd_type = TYPE_E; 822 cmd_form = FORM_N; 823 } 824 | E SP form_code 825 { 826 cmd_type = TYPE_E; 827 cmd_form = $3; 828 } 829 | I 830 { 831 cmd_type = TYPE_I; 832 } 833 | L 834 { 835 cmd_type = TYPE_L; 836 cmd_bytesz = NBBY; 837 } 838 | L SP byte_size 839 { 840 cmd_type = TYPE_L; 841 cmd_bytesz = $3; 842 } 843 /* this is for a bug in the BBN ftp */ 844 | L byte_size 845 { 846 cmd_type = TYPE_L; 847 cmd_bytesz = $2; 848 } 849 ; 850 851struct_code 852 : F 853 { 854 $$ = STRU_F; 855 } 856 | R 857 { 858 $$ = STRU_R; 859 } 860 | P 861 { 862 $$ = STRU_P; 863 } 864 ; 865 866mode_code 867 : S 868 { 869 $$ = MODE_S; 870 } 871 | B 872 { 873 $$ = MODE_B; 874 } 875 | C 876 { 877 $$ = MODE_C; 878 } 879 ; 880 881pathname 882 : pathstring 883 { 884 /* 885 * Problem: this production is used for all pathname 886 * processing, but only gives a 550 error reply. 887 * This is a valid reply in some cases but not in others. 888 */ 889 if (logged_in && $1 && strchr($1, '~') != NULL) { 890 glob_t gl; 891 int flags = 892 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 893 char *pptr = $1; 894 895 /* 896 * glob() will only find a leading ~, but 897 * Netscape kindly puts a slash in front of 898 * it for publish URLs. There needs to be 899 * a flag for glob() that expands tildes 900 * anywhere in the string. 901 */ 902 if ((pptr[0] == '/') && (pptr[1] == '~')) 903 pptr++; 904 905 memset(&gl, 0, sizeof(gl)); 906 if (glob(pptr, flags, NULL, &gl) || 907 gl.gl_pathc == 0) { 908 reply(550, "not found"); 909 $$ = NULL; 910 } else { 911 $$ = strdup(gl.gl_pathv[0]); 912 } 913 globfree(&gl); 914 free($1); 915 } else 916 $$ = $1; 917 } 918 ; 919 920pathstring 921 : STRING 922 ; 923 924octal_number 925 : NUMBER 926 { 927 int ret, dec, multby, digit; 928 929 /* 930 * Convert a number that was read as decimal number 931 * to what it would be if it had been read as octal. 932 */ 933 dec = $1; 934 multby = 1; 935 ret = 0; 936 while (dec) { 937 digit = dec%10; 938 if (digit > 7) { 939 ret = -1; 940 break; 941 } 942 ret += digit * multby; 943 multby *= 8; 944 dec /= 10; 945 } 946 $$ = ret; 947 } 948 ; 949 950 951check_login 952 : /* empty */ 953 { 954 if (logged_in) 955 $$ = 1; 956 else { 957 reply(530, "Please login with USER and PASS."); 958 $$ = 0; 959 } 960 } 961 ; 962 963check_login_epsvall 964 : /* empty */ 965 { 966 if (!logged_in) { 967 reply(530, "Please login with USER and PASS."); 968 $$ = 0; 969 } else if (epsvall) { 970 reply(501, "the command is disallowed " 971 "after EPSV ALL"); 972 usedefault = 1; 973 $$ = 0; 974 } else 975 $$ = 1; 976 } 977 ; 978 979%% 980 981#define CMD 0 /* beginning of command */ 982#define ARGS 1 /* expect miscellaneous arguments */ 983#define STR1 2 /* expect SP followed by STRING */ 984#define STR2 3 /* expect STRING */ 985#define OSTR 4 /* optional SP then STRING */ 986#define ZSTR1 5 /* SP then optional STRING */ 987#define ZSTR2 6 /* optional STRING after SP */ 988#define SITECMD 7 /* SITE command */ 989#define NSTR 8 /* Number followed by a string */ 990 991struct tab { 992 char *name; 993 short token; 994 short state; 995 short implemented; /* 1 if command is implemented */ 996 char *help; 997}; 998 999struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1000 { "USER", USER, STR1, 1, "<sp> username" }, 1001 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1002 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1003 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1004 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1005 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1006 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 1007 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1008 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1009 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 1010 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1011 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1012 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1013 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1014 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1015 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1016 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1017 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1018 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1019 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1020 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1021 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1022 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1023 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1024 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1025 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1026 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1027 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1028 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1029 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1030 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1031 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1032 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1033 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1034 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1035 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1036 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1037 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1038 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1039 { "NOOP", NOOP, ARGS, 1, "" }, 1040 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1041 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1042 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1043 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1044 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1045 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1046 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1047 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1048 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1049 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1050 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1051 { NULL, 0, 0, 0, 0 } 1052}; 1053 1054struct tab sitetab[] = { 1055 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1056 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1057 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1058 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1059 { NULL, 0, 0, 0, 0 } 1060}; 1061 1062static void help __P((struct tab *, char *)); 1063static struct tab * 1064 lookup __P((struct tab *, char *)); 1065static void sizecmd __P((char *)); 1066static int yylex __P((void)); 1067 1068extern int epsvall; 1069 1070static struct tab * 1071lookup(p, cmd) 1072 struct tab *p; 1073 char *cmd; 1074{ 1075 1076 for (; p->name != NULL; p++) 1077 if (strcmp(cmd, p->name) == 0) 1078 return (p); 1079 return (0); 1080} 1081 1082#include <arpa/telnet.h> 1083 1084/* 1085 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1086 */ 1087char * 1088getline(s, n, iop) 1089 char *s; 1090 int n; 1091 FILE *iop; 1092{ 1093 int c; 1094 char *cs; 1095 1096 cs = s; 1097/* tmpline may contain saved command from urgent mode interruption */ 1098 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1099 *cs++ = tmpline[c]; 1100 if (tmpline[c] == '\n') { 1101 *cs++ = '\0'; 1102 if (debug) 1103 syslog(LOG_DEBUG, "command: %s", s); 1104 tmpline[0] = '\0'; 1105 return(s); 1106 } 1107 if (c == 0) 1108 tmpline[0] = '\0'; 1109 } 1110 while ((c = getc(iop)) != EOF) { 1111 c &= 0377; 1112 if (c == IAC) { 1113 if ((c = getc(iop)) != EOF) { 1114 c &= 0377; 1115 switch (c) { 1116 case WILL: 1117 case WONT: 1118 c = getc(iop); 1119 printf("%c%c%c", IAC, DONT, 0377&c); 1120 (void) fflush(stdout); 1121 continue; 1122 case DO: 1123 case DONT: 1124 c = getc(iop); 1125 printf("%c%c%c", IAC, WONT, 0377&c); 1126 (void) fflush(stdout); 1127 continue; 1128 case IAC: 1129 break; 1130 default: 1131 continue; /* ignore command */ 1132 } 1133 } 1134 } 1135 *cs++ = c; 1136 if (--n <= 0 || c == '\n') 1137 break; 1138 } 1139 if (c == EOF && cs == s) 1140 return (NULL); 1141 *cs++ = '\0'; 1142 if (debug) { 1143 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1144 /* Don't syslog passwords */ 1145 syslog(LOG_DEBUG, "command: %.5s ???", s); 1146 } else { 1147 char *cp; 1148 int len; 1149 1150 /* Don't syslog trailing CR-LF */ 1151 len = strlen(s); 1152 cp = s + len - 1; 1153 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1154 --cp; 1155 --len; 1156 } 1157 syslog(LOG_DEBUG, "command: %.*s", len, s); 1158 } 1159 } 1160 return (s); 1161} 1162 1163void 1164toolong(signo) 1165 int signo; 1166{ 1167 struct syslog_data sdata = SYSLOG_DATA_INIT; 1168 1169 /* XXX signal races */ 1170 reply(421, 1171 "Timeout (%d seconds): closing control connection.", timeout); 1172 if (logging) 1173 syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds", 1174 (pw ? pw -> pw_name : "unknown"), timeout); 1175 dologout(1); 1176} 1177 1178static int 1179yylex() 1180{ 1181 static int cpos; 1182 char *cp, *cp2; 1183 struct tab *p; 1184 int n; 1185 char c; 1186 1187 for (;;) { 1188 switch (state) { 1189 1190 case CMD: 1191 (void) signal(SIGALRM, toolong); 1192 (void) alarm((unsigned) timeout); 1193 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1194 reply(221, "You could at least say goodbye."); 1195 dologout(0); 1196 } 1197 (void) alarm(0); 1198 if ((cp = strchr(cbuf, '\r'))) { 1199 *cp++ = '\n'; 1200 *cp = '\0'; 1201 } 1202#ifdef HASSETPROCTITLE 1203 if (strncasecmp(cbuf, "PASS", 4) != 0) { 1204 if ((cp = strpbrk(cbuf, "\n"))) { 1205 c = *cp; 1206 *cp = '\0'; 1207 setproctitle("%s: %s", proctitle, cbuf); 1208 *cp = c; 1209 } 1210 } 1211#endif /* HASSETPROCTITLE */ 1212 if ((cp = strpbrk(cbuf, " \n"))) 1213 cpos = cp - cbuf; 1214 if (cpos == 0) 1215 cpos = 4; 1216 c = cbuf[cpos]; 1217 cbuf[cpos] = '\0'; 1218 upper(cbuf); 1219 p = lookup(cmdtab, cbuf); 1220 cbuf[cpos] = c; 1221 if (p != 0) { 1222 if (p->implemented == 0) { 1223 nack(p->name); 1224 return (LEXERR); 1225 } 1226 state = p->state; 1227 yylval.s = p->name; 1228 return (p->token); 1229 } 1230 break; 1231 1232 case SITECMD: 1233 if (cbuf[cpos] == ' ') { 1234 cpos++; 1235 return (SP); 1236 } 1237 cp = &cbuf[cpos]; 1238 if ((cp2 = strpbrk(cp, " \n"))) 1239 cpos = cp2 - cbuf; 1240 c = cbuf[cpos]; 1241 cbuf[cpos] = '\0'; 1242 upper(cp); 1243 p = lookup(sitetab, cp); 1244 cbuf[cpos] = c; 1245 if (p != 0) { 1246 if (p->implemented == 0) { 1247 state = CMD; 1248 nack(p->name); 1249 return (LEXERR); 1250 } 1251 state = p->state; 1252 yylval.s = p->name; 1253 return (p->token); 1254 } 1255 state = CMD; 1256 break; 1257 1258 case OSTR: 1259 if (cbuf[cpos] == '\n') { 1260 state = CMD; 1261 return (CRLF); 1262 } 1263 /* FALLTHROUGH */ 1264 1265 case STR1: 1266 case ZSTR1: 1267 dostr1: 1268 if (cbuf[cpos] == ' ') { 1269 cpos++; 1270 state = state == OSTR ? STR2 : state+1; 1271 return (SP); 1272 } 1273 break; 1274 1275 case ZSTR2: 1276 if (cbuf[cpos] == '\n') { 1277 state = CMD; 1278 return (CRLF); 1279 } 1280 /* FALLTHROUGH */ 1281 1282 case STR2: 1283 cp = &cbuf[cpos]; 1284 n = strlen(cp); 1285 cpos += n - 1; 1286 /* 1287 * Make sure the string is nonempty and \n terminated. 1288 */ 1289 if (n > 1 && cbuf[cpos] == '\n') { 1290 cbuf[cpos] = '\0'; 1291 yylval.s = strdup(cp); 1292 if (yylval.s == NULL) 1293 fatal("Ran out of memory."); 1294 cbuf[cpos] = '\n'; 1295 state = ARGS; 1296 return (STRING); 1297 } 1298 break; 1299 1300 case NSTR: 1301 if (cbuf[cpos] == ' ') { 1302 cpos++; 1303 return (SP); 1304 } 1305 if (isdigit(cbuf[cpos])) { 1306 cp = &cbuf[cpos]; 1307 while (isdigit(cbuf[++cpos])) 1308 ; 1309 c = cbuf[cpos]; 1310 cbuf[cpos] = '\0'; 1311 yylval.i = atoi(cp); 1312 cbuf[cpos] = c; 1313 state = STR1; 1314 return (NUMBER); 1315 } 1316 state = STR1; 1317 goto dostr1; 1318 1319 case ARGS: 1320 if (isdigit(cbuf[cpos])) { 1321 cp = &cbuf[cpos]; 1322 while (isdigit(cbuf[++cpos])) 1323 ; 1324 c = cbuf[cpos]; 1325 cbuf[cpos] = '\0'; 1326 yylval.i = atoi(cp); 1327 cbuf[cpos] = c; 1328 return (NUMBER); 1329 } 1330 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1331 && !isalnum(cbuf[cpos + 3])) { 1332 yylval.s = strdup("ALL"); 1333 cpos += 3; 1334 return ALL; 1335 } 1336 switch (cbuf[cpos++]) { 1337 1338 case '\n': 1339 state = CMD; 1340 return (CRLF); 1341 1342 case ' ': 1343 return (SP); 1344 1345 case ',': 1346 return (COMMA); 1347 1348 case 'A': 1349 case 'a': 1350 return (A); 1351 1352 case 'B': 1353 case 'b': 1354 return (B); 1355 1356 case 'C': 1357 case 'c': 1358 return (C); 1359 1360 case 'E': 1361 case 'e': 1362 return (E); 1363 1364 case 'F': 1365 case 'f': 1366 return (F); 1367 1368 case 'I': 1369 case 'i': 1370 return (I); 1371 1372 case 'L': 1373 case 'l': 1374 return (L); 1375 1376 case 'N': 1377 case 'n': 1378 return (N); 1379 1380 case 'P': 1381 case 'p': 1382 return (P); 1383 1384 case 'R': 1385 case 'r': 1386 return (R); 1387 1388 case 'S': 1389 case 's': 1390 return (S); 1391 1392 case 'T': 1393 case 't': 1394 return (T); 1395 1396 } 1397 break; 1398 1399 default: 1400 fatal("Unknown state in scanner."); 1401 } 1402 state = CMD; 1403 return (LEXERR); 1404 } 1405} 1406 1407void 1408upper(s) 1409 char *s; 1410{ 1411 while (*s != '\0') { 1412 if (islower(*s)) 1413 *s = toupper(*s); 1414 s++; 1415 } 1416} 1417 1418static void 1419help(ctab, s) 1420 struct tab *ctab; 1421 char *s; 1422{ 1423 struct tab *c; 1424 int width, NCMDS; 1425 char *type; 1426 1427 if (ctab == sitetab) 1428 type = "SITE "; 1429 else 1430 type = ""; 1431 width = 0, NCMDS = 0; 1432 for (c = ctab; c->name != NULL; c++) { 1433 int len = strlen(c->name); 1434 1435 if (len > width) 1436 width = len; 1437 NCMDS++; 1438 } 1439 width = (width + 8) &~ 7; 1440 if (s == 0) { 1441 int i, j, w; 1442 int columns, lines; 1443 1444 lreply(214, "The following %scommands are recognized %s.", 1445 type, "(* =>'s unimplemented)"); 1446 columns = 76 / width; 1447 if (columns == 0) 1448 columns = 1; 1449 lines = (NCMDS + columns - 1) / columns; 1450 for (i = 0; i < lines; i++) { 1451 printf(" "); 1452 for (j = 0; j < columns; j++) { 1453 c = ctab + j * lines + i; 1454 printf("%s%c", c->name, 1455 c->implemented ? ' ' : '*'); 1456 if (c + lines >= &ctab[NCMDS]) 1457 break; 1458 w = strlen(c->name) + 1; 1459 while (w < width) { 1460 putchar(' '); 1461 w++; 1462 } 1463 } 1464 printf("\r\n"); 1465 } 1466 (void) fflush(stdout); 1467 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1468 return; 1469 } 1470 upper(s); 1471 c = lookup(ctab, s); 1472 if (c == (struct tab *)0) { 1473 reply(502, "Unknown command %s.", s); 1474 return; 1475 } 1476 if (c->implemented) 1477 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1478 else 1479 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1480 c->name, c->help); 1481} 1482 1483static void 1484sizecmd(filename) 1485 char *filename; 1486{ 1487 switch (type) { 1488 case TYPE_L: 1489 case TYPE_I: { 1490 struct stat stbuf; 1491 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1492 reply(550, "%s: not a plain file.", filename); 1493 else 1494 reply(213, "%qu", stbuf.st_size); 1495 break; } 1496 case TYPE_A: { 1497 FILE *fin; 1498 int c; 1499 off_t count; 1500 struct stat stbuf; 1501 fin = fopen(filename, "r"); 1502 if (fin == NULL) { 1503 perror_reply(550, filename); 1504 return; 1505 } 1506 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1507 reply(550, "%s: not a plain file.", filename); 1508 (void) fclose(fin); 1509 return; 1510 } 1511 1512 count = 0; 1513 while((c=getc(fin)) != EOF) { 1514 if (c == '\n') /* will get expanded to \r\n */ 1515 count++; 1516 count++; 1517 } 1518 (void) fclose(fin); 1519 1520 reply(213, "%qd", count); 1521 break; } 1522 default: 1523 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1524 } 1525} 1526