ftpcmd.y revision 233294
118334Speter/* $NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $ */ 290075Sobrien 3169689Skan/* 418334Speter * Copyright (c) 1985, 1988, 1993, 1994 590075Sobrien * The Regents of the University of California. All rights reserved. 618334Speter * 790075Sobrien * Redistribution and use in source and binary forms, with or without 890075Sobrien * modification, are permitted provided that the following conditions 990075Sobrien * are met: 1090075Sobrien * 1. Redistributions of source code must retain the above copyright 1118334Speter * notice, this list of conditions and the following disclaimer. 1290075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1390075Sobrien * notice, this list of conditions and the following disclaimer in the 1490075Sobrien * documentation and/or other materials provided with the distribution. 1590075Sobrien * 3. All advertising materials mentioning features or use of this software 1618334Speter * must display the following acknowledgement: 1718334Speter * This product includes software developed by the University of 1890075Sobrien * California, Berkeley and its contributors. 19169689Skan * 4. Neither the name of the University nor the names of its contributors 20169689Skan * may be used to endorse or promote products derived from this software 2118334Speter * without specific prior written permission. 22117395Skan * 23117395Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24117395Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25223262Sbenl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26223262Sbenl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27223262Sbenl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28223262Sbenl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29223262Sbenl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3118334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32132718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33169689Skan * SUCH DAMAGE. 34169689Skan * 3518334Speter * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 3618334Speter */ 3718334Speter 3818334Speter/* 3918334Speter * Grammar for FTP commands. 4018334Speter * See RFC 959. 4118334Speter */ 4218334Speter 4318334Speter%{ 44169689Skan 4518334Speter#include "ftpd_locl.h" 4618334SpeterRCSID("$Id$"); 4718334Speter 4818334Speteroff_t restart_point; 49169689Skan 50169689Skanstatic int hasyyerrored; 51169689Skan 5290075Sobrien 5390075Sobrienstatic int cmd_type; 5418334Speterstatic int cmd_form; 55169689Skanstatic int cmd_bytesz; 5618334Speterchar cbuf[64*1024]; 57169689Skanchar *fromname; 58169689Skan 59169689Skanstruct tab { 60169689Skan char *name; 61169689Skan short token; 62169689Skan short state; 63169689Skan short implemented; /* 1 if command is implemented */ 64169689Skan char *help; 65169689Skan}; 66169689Skan 67169689Skanextern struct tab cmdtab[]; 68169689Skanextern struct tab sitetab[]; 69169689Skan 70169689Skanstatic char *copy (char *); 71169689Skanstatic void help (struct tab *, char *); 72169689Skanstatic struct tab * 73169689Skan lookup (struct tab *, char *); 74169689Skanstatic void sizecmd (char *); 75169689Skanstatic RETSIGTYPE toolong (int); 76169689Skanstatic int yylex (void); 77169689Skan 78169689Skan/* This is for bison */ 79169689Skan 80169689Skan#if !defined(alloca) && !defined(HAVE_ALLOCA) 81169689Skan#define alloca(x) malloc(x) 82169689Skan#endif 83169689Skan 84169689Skan%} 8550397Sobrien 86169689Skan%union { 8750397Sobrien int i; 8818334Speter char *s; 89169689Skan} 90169689Skan 91169689Skan%token 92169689Skan A B C E F I 93169689Skan L N P R S T 94169689Skan 95169689Skan SP CRLF COMMA 96169689Skan 97169689Skan USER PASS ACCT REIN QUIT PORT 98169689Skan PASV TYPE STRU MODE RETR STOR 99169689Skan APPE MLFL MAIL MSND MSOM MSAM 100169689Skan MRSQ MRCP ALLO REST RNFR RNTO 101169689Skan ABOR DELE CWD LIST NLST SITE 102169689Skan sTAT HELP NOOP MKD RMD PWD 103169689Skan CDUP STOU SMNT SYST SIZE MDTM 104169689Skan EPRT EPSV 105169689Skan 106169689Skan UMASK IDLE CHMOD 107169689Skan 108169689Skan AUTH ADAT PROT PBSZ CCC MIC 109169689Skan CONF ENC 110169689Skan 111169689Skan KAUTH KLIST KDESTROY KRBTKFILE AFSLOG 112169689Skan LOCATE URL 113169689Skan 114169689Skan FEAT OPTS 115169689Skan 116169689Skan LEXERR 117169689Skan 118169689Skan%token <s> STRING 119169689Skan%token <i> NUMBER 120169689Skan 121169689Skan%type <i> check_login check_login_no_guest check_secure octal_number byte_size 122169689Skan%type <i> struct_code mode_code type_code form_code 123169689Skan%type <s> pathstring pathname password username 124169689Skan 125169689Skan%start cmd_list 126169689Skan 127169689Skan%% 128169689Skan 129169689Skancmd_list 130169689Skan : /* empty */ 131169689Skan | cmd_list cmd 132169689Skan { 133169689Skan fromname = (char *) 0; 134169689Skan restart_point = (off_t) 0; 135169689Skan } 136169689Skan | cmd_list rcmd 137169689Skan ; 138169689Skan 139169689Skancmd 140169689Skan : USER SP username CRLF check_secure 141169689Skan { 142169689Skan if ($5) 143169689Skan user($3); 144169689Skan free($3); 145169689Skan } 146169689Skan | PASS SP password CRLF check_secure 147169689Skan { 148169689Skan if ($5) 149169689Skan pass($3); 150169689Skan memset ($3, 0, strlen($3)); 151169689Skan free($3); 152169689Skan } 153169689Skan 154169689Skan | PORT SP host_port CRLF check_secure 155169689Skan { 156169689Skan if ($5) { 157169689Skan if (paranoid && 158169689Skan (data_dest->sa_family != his_addr->sa_family || 159169689Skan (socket_get_port(data_dest) < IPPORT_RESERVED) || 160169689Skan memcmp(socket_get_address(data_dest), 161169689Skan socket_get_address(his_addr), 162169689Skan socket_addr_size(his_addr)) != 0)) { 163169689Skan usedefault = 1; 164169689Skan reply(500, "Illegal PORT range rejected."); 165117395Skan } else { 16650397Sobrien usedefault = 0; 16750397Sobrien if (pdata >= 0) { 168169689Skan close(pdata); 169169689Skan pdata = -1; 17050397Sobrien } 171132718Skan reply(200, "PORT command successful."); 172132718Skan } 173132718Skan } 174132718Skan } 175169689Skan | EPRT SP STRING CRLF check_secure 176169689Skan { 177169689Skan if ($5) 178169689Skan eprt ($3); 179169689Skan free ($3); 180169689Skan } 181169689Skan | PASV CRLF check_login 182169689Skan { 183169689Skan if($3) 184169689Skan pasv (); 185169689Skan } 186169689Skan | EPSV CRLF check_login 187169689Skan { 188169689Skan if($3) 18918334Speter epsv (NULL); 19018334Speter } 191117395Skan | EPSV SP STRING CRLF check_login 19290075Sobrien { 19318334Speter if($5) 19418334Speter epsv ($3); 19518334Speter free ($3); 196117395Skan } 197169689Skan | TYPE SP type_code CRLF check_secure 198169689Skan { 199169689Skan if ($5) { 200169689Skan switch (cmd_type) { 201169689Skan 202169689Skan case TYPE_A: 20318334Speter if (cmd_form == FORM_N) { 20490075Sobrien reply(200, "Type set to A."); 20590075Sobrien type = cmd_type; 20690075Sobrien form = cmd_form; 20790075Sobrien } else 20890075Sobrien reply(504, "Form must be N."); 20990075Sobrien break; 21090075Sobrien 21190075Sobrien case TYPE_E: 21290075Sobrien reply(504, "Type E not implemented."); 21390075Sobrien break; 21490075Sobrien 21590075Sobrien case TYPE_I: 21690075Sobrien reply(200, "Type set to I."); 21718334Speter type = cmd_type; 21818334Speter break; 21918334Speter 220169689Skan case TYPE_L: 22118334Speter#if NBBY == 8 22218334Speter if (cmd_bytesz == 8) { 22390075Sobrien reply(200, 22418334Speter "Type set to L (byte size 8)."); 225169689Skan type = cmd_type; 226169689Skan } else 227169689Skan reply(504, "Byte size must be 8."); 228169689Skan#else /* NBBY == 8 */ 229169689Skan UNIMPLEMENTED for NBBY != 8 230169689Skan#endif /* NBBY == 8 */ 231169689Skan } 232169689Skan } 233169689Skan } 234169689Skan | STRU SP struct_code CRLF check_secure 235169689Skan { 236169689Skan if ($5) { 237169689Skan switch ($3) { 238169689Skan 23990075Sobrien case STRU_F: 24090075Sobrien reply(200, "STRU F ok."); 24190075Sobrien break; 24290075Sobrien 24350397Sobrien default: 24490075Sobrien reply(504, "Unimplemented STRU type."); 245169689Skan } 24618334Speter } 247169689Skan } 248169689Skan | MODE SP mode_code CRLF check_secure 249169689Skan { 250169689Skan if ($5) { 251169689Skan switch ($3) { 252169689Skan 253169689Skan case MODE_S: 254169689Skan reply(200, "MODE S ok."); 255169689Skan break; 256169689Skan 257169689Skan default: 258169689Skan reply(502, "Unimplemented MODE type."); 259169689Skan } 260169689Skan } 261169689Skan } 262169689Skan | ALLO SP NUMBER CRLF check_secure 263169689Skan { 264169689Skan if ($5) { 265169689Skan reply(202, "ALLO command ignored."); 266169689Skan } 267169689Skan } 26890075Sobrien | ALLO SP NUMBER SP R SP NUMBER CRLF check_secure 269132718Skan { 270132718Skan if ($9) { 27118334Speter reply(202, "ALLO command ignored."); 272169689Skan } 273169689Skan } 274169689Skan | RETR SP pathname CRLF check_login 275169689Skan { 276169689Skan char *name = $3; 277169689Skan 278169689Skan if ($5 && name != NULL) 279169689Skan retrieve(0, name); 280169689Skan if (name != NULL) 281169689Skan free(name); 282169689Skan } 283169689Skan | STOR SP pathname CRLF check_login 284169689Skan { 285169689Skan char *name = $3; 286169689Skan 287169689Skan if ($5 && name != NULL) 288169689Skan do_store(name, "w", 0); 289169689Skan if (name != NULL) 290169689Skan free(name); 291169689Skan } 292169689Skan | APPE SP pathname CRLF check_login 293169689Skan { 294169689Skan char *name = $3; 295169689Skan 296169689Skan if ($5 && name != NULL) 297169689Skan do_store(name, "a", 0); 298169689Skan if (name != NULL) 299169689Skan free(name); 300169689Skan } 301169689Skan | NLST CRLF check_login 302169689Skan { 303169689Skan if ($3) 304169689Skan send_file_list("."); 305169689Skan } 306169689Skan | NLST SP STRING CRLF check_login 307169689Skan { 308169689Skan char *name = $3; 309169689Skan 310169689Skan if ($5 && name != NULL) 311169689Skan send_file_list(name); 312169689Skan if (name != NULL) 313169689Skan free(name); 314169689Skan } 315169689Skan | LIST CRLF check_login 316169689Skan { 317169689Skan if($3) 318169689Skan list_file("."); 319169689Skan } 320169689Skan | LIST SP pathname CRLF check_login 321169689Skan { 322169689Skan if($5) 323169689Skan list_file($3); 324169689Skan free($3); 325169689Skan } 326169689Skan | sTAT SP pathname CRLF check_login 327169689Skan { 328169689Skan if ($5 && $3 != NULL) 329169689Skan statfilecmd($3); 330169689Skan if ($3 != NULL) 331169689Skan free($3); 33218334Speter } 33318334Speter | sTAT CRLF check_secure 33418334Speter { 33518334Speter if ($3) 33618334Speter statcmd(); 33718334Speter } 33818334Speter | DELE SP pathname CRLF check_login_no_guest 33918334Speter { 34018334Speter if ($5 && $3 != NULL) 34118334Speter do_delete($3); 34218334Speter if ($3 != NULL) 34318334Speter free($3); 34418334Speter } 34518334Speter | RNTO SP pathname CRLF check_login_no_guest 34618334Speter { 34718334Speter if($5){ 34818334Speter if (fromname) { 34918334Speter renamecmd(fromname, $3); 35018334Speter free(fromname); 35190075Sobrien fromname = (char *) 0; 35290075Sobrien } else { 353169689Skan reply(503, "Bad sequence of commands."); 35418334Speter } 355117395Skan } 35618334Speter if ($3 != NULL) 35790075Sobrien free($3); 35890075Sobrien } 359169689Skan | ABOR CRLF check_secure 36096263Sobrien { 36190075Sobrien if ($3) 36296263Sobrien reply(225, "ABOR command successful."); 36318334Speter } 36418334Speter | CWD CRLF check_login 36518334Speter { 36618334Speter if ($3) { 36718334Speter const char *path = pw->pw_dir; 36818334Speter if (dochroot || guest) 36918334Speter path = "/"; 370169689Skan cwd(path); 37118334Speter } 37218334Speter } 37390075Sobrien | CWD SP pathname CRLF check_login 37418334Speter { 37518334Speter if ($5 && $3 != NULL) 37618334Speter cwd($3); 37718334Speter if ($3 != NULL) 37890075Sobrien free($3); 379169689Skan } 38018334Speter | HELP CRLF check_secure 38118334Speter { 38218334Speter if ($3) 38318334Speter help(cmdtab, (char *) 0); 38418334Speter } 38518334Speter | HELP SP STRING CRLF check_secure 38618334Speter { 38718334Speter if ($5) { 388169689Skan char *cp = $3; 389260918Spfg 390260918Spfg if (strncasecmp(cp, "SITE", 4) == 0) { 39118334Speter cp = $3 + 4; 39218334Speter if (*cp == ' ') 39350397Sobrien cp++; 39450397Sobrien if (*cp) 39550397Sobrien help(sitetab, cp); 39650397Sobrien else 39750397Sobrien help(sitetab, (char *) 0); 39850397Sobrien } else 39950397Sobrien help(cmdtab, $3); 400132718Skan } 40190075Sobrien } 40290075Sobrien | NOOP CRLF check_secure 40390075Sobrien { 404169689Skan if ($3) 405169689Skan reply(200, "NOOP command successful."); 40650397Sobrien } 40750397Sobrien | MKD SP pathname CRLF check_login 40850397Sobrien { 40950397Sobrien if ($5 && $3 != NULL) 41050397Sobrien makedir($3); 411169689Skan if ($3 != NULL) 412169689Skan free($3); 41350397Sobrien } 41496263Sobrien | RMD SP pathname CRLF check_login_no_guest 41550397Sobrien { 41650397Sobrien if ($5 && $3 != NULL) 41796263Sobrien removedir($3); 418169689Skan if ($3 != NULL) 419169689Skan free($3); 420169689Skan } 421169689Skan | PWD CRLF check_login 422169689Skan { 423169689Skan if ($3) 424169689Skan pwd(); 42550397Sobrien } 42650397Sobrien | CDUP CRLF check_login 42750397Sobrien { 42850397Sobrien if ($3) 42996263Sobrien cwd(".."); 43050397Sobrien } 43190075Sobrien | FEAT CRLF check_secure 432169689Skan { 433169689Skan if ($3) { 434169689Skan lreply(211, "Supported features:"); 435169689Skan lreply(0, " MDTM"); 436169689Skan lreply(0, " REST STREAM"); 437169689Skan lreply(0, " SIZE"); 438169689Skan reply(211, "End"); 439169689Skan } 440169689Skan } 441169689Skan | OPTS SP STRING CRLF check_secure 44250397Sobrien { 44350397Sobrien if ($5) 44450397Sobrien reply(501, "Bad options"); 44550397Sobrien free ($3); 44696263Sobrien } 447169689Skan 448132718Skan | SITE SP HELP CRLF check_secure 449169689Skan { 450169689Skan if ($5) 451169689Skan help(sitetab, (char *) 0); 452169689Skan } 453169689Skan | SITE SP HELP SP STRING CRLF check_secure 454169689Skan { 455169689Skan if ($7) 456169689Skan help(sitetab, $5); 45750397Sobrien } 45850397Sobrien | SITE SP UMASK CRLF check_login 45950397Sobrien { 46050397Sobrien if ($5) { 46150397Sobrien int oldmask = umask(0); 46296263Sobrien umask(oldmask); 463132718Skan reply(200, "Current UMASK is %03o", oldmask); 464169689Skan } 46550397Sobrien } 46650397Sobrien | SITE SP UMASK SP octal_number CRLF check_login_no_guest 46750397Sobrien { 46850397Sobrien if ($7) { 46950397Sobrien if (($5 == -1) || ($5 > 0777)) { 470169689Skan reply(501, "Bad UMASK value"); 471169689Skan } else { 47250397Sobrien int oldmask = umask($5); 473169689Skan reply(200, 474169689Skan "UMASK set to %03o (was %03o)", 475169689Skan $5, oldmask); 47650397Sobrien } 47750397Sobrien } 47850397Sobrien } 47950397Sobrien | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest 48050397Sobrien { 48150397Sobrien if ($9 && $7 != NULL) { 48250397Sobrien if ($5 > 0777) 48350397Sobrien reply(501, 48450397Sobrien "CHMOD: Mode value must be between 0 and 0777"); 48550397Sobrien else if (chmod($7, $5) < 0) 48650397Sobrien perror_reply(550, $7); 48750397Sobrien else 48850397Sobrien reply(200, "CHMOD command successful."); 48950397Sobrien } 49050397Sobrien if ($7 != NULL) 49150397Sobrien free($7); 49250397Sobrien } 49350397Sobrien | SITE SP IDLE CRLF check_secure 494169689Skan { 495169689Skan if ($5) 496169689Skan reply(200, 497169689Skan "Current IDLE time limit is %d seconds; max %d", 49850397Sobrien ftpd_timeout, maxtimeout); 49950397Sobrien } 50050397Sobrien | SITE SP IDLE SP NUMBER CRLF check_secure 501169689Skan { 502169689Skan if ($7) { 503169689Skan if ($5 < 30 || $5 > maxtimeout) { 504169689Skan reply(501, 505169689Skan "Maximum IDLE time must be between 30 and %d seconds", 506169689Skan maxtimeout); 50750397Sobrien } else { 50850397Sobrien ftpd_timeout = $5; 50950397Sobrien alarm((unsigned) ftpd_timeout); 51050397Sobrien reply(200, 51150397Sobrien "Maximum IDLE time set to %d seconds", 512169689Skan ftpd_timeout); 51350397Sobrien } 51450397Sobrien } 51550397Sobrien } 51650397Sobrien 51750397Sobrien | SITE SP KAUTH SP STRING CRLF check_login 51850397Sobrien { 51990075Sobrien reply(500, "Command not implemented."); 52050397Sobrien } 52190075Sobrien | SITE SP KLIST CRLF check_login 52290075Sobrien { 52350397Sobrien if($5) 524132718Skan klist(); 52590075Sobrien } 52690075Sobrien | SITE SP KDESTROY CRLF check_login 527169689Skan { 528169689Skan reply(500, "Command not implemented."); 529169689Skan } 53090075Sobrien | SITE SP KRBTKFILE SP STRING CRLF check_login 53190075Sobrien { 53290075Sobrien reply(500, "Command not implemented."); 53390075Sobrien } 534132718Skan | SITE SP AFSLOG CRLF check_login 535169689Skan { 536169689Skan#if defined(KRB5) 537169689Skan if(guest) 538260918Spfg reply(500, "Can't be done as guest."); 539260918Spfg else if($5) 540260918Spfg afslog(NULL, 0); 541260918Spfg#else 542260918Spfg reply(500, "Command not implemented."); 543260918Spfg#endif 544260918Spfg } 545169689Skan | SITE SP AFSLOG SP STRING CRLF check_login 546169689Skan { 547169689Skan#if defined(KRB5) 548169689Skan if(guest) 549169689Skan reply(500, "Can't be done as guest."); 550169689Skan else if($7) 551169689Skan afslog($5, 0); 552169689Skan if($5) 553169689Skan free($5); 554169689Skan#else 555169689Skan reply(500, "Command not implemented."); 556169689Skan#endif 557169689Skan } 55890075Sobrien | SITE SP LOCATE SP STRING CRLF check_login 559169689Skan { 560169689Skan if($7 && $5 != NULL) 561169689Skan find($5); 562169689Skan if($5 != NULL) 563169689Skan free($5); 564169689Skan } 565169689Skan | SITE SP URL CRLF check_secure 56690075Sobrien { 56718334Speter if ($5) 56818334Speter reply(200, "http://www.pdc.kth.se/heimdal/"); 56918334Speter } 57018334Speter | STOU SP pathname CRLF check_login 57118334Speter { 57218334Speter if ($5 && $3 != NULL) 573132718Skan do_store($3, "w", 1); 57418334Speter if ($3 != NULL) 57550397Sobrien free($3); 576169689Skan } 57790075Sobrien | SYST CRLF check_secure 57850397Sobrien { 579132718Skan if ($3) { 580132718Skan#if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__) 581132718Skan reply(215, "UNIX Type: L%d", NBBY); 582169689Skan#else 583169689Skan reply(215, "UNKNOWN Type: L%d", NBBY); 58490075Sobrien#endif 585132718Skan } 586169689Skan } 587169689Skan 588169689Skan /* 589169689Skan * SIZE is not in RFC959, but Postel has blessed it and 590169689Skan * it will be in the updated RFC. 591169689Skan * 592169689Skan * Return size of file in a format suitable for 593169689Skan * using with RESTART (we just count bytes). 594169689Skan */ 595169689Skan | SIZE SP pathname CRLF check_login 596169689Skan { 597169689Skan if ($5 && $3 != NULL) 598169689Skan sizecmd($3); 599169689Skan if ($3 != NULL) 600169689Skan free($3); 601169689Skan } 602169689Skan 603169689Skan /* 604169689Skan * MDTM is not in RFC959, but Postel has blessed it and 605169689Skan * it will be in the updated RFC. 606169689Skan * 607169689Skan * Return modification time of file as an ISO 3307 608169689Skan * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 609169689Skan * where xxx is the fractional second (of any precision, 610169689Skan * not necessarily 3 digits) 611169689Skan */ 612169689Skan | MDTM SP pathname CRLF check_login 613169689Skan { 614169689Skan if ($5 && $3 != NULL) { 615169689Skan struct stat stbuf; 616169689Skan if (stat($3, &stbuf) < 0) 617169689Skan reply(550, "%s: %s", 618169689Skan $3, strerror(errno)); 619169689Skan else if (!S_ISREG(stbuf.st_mode)) { 620169689Skan reply(550, 621169689Skan "%s: not a plain file.", $3); 622169689Skan } else { 623169689Skan struct tm *t; 624169689Skan time_t mtime = stbuf.st_mtime; 625169689Skan 626169689Skan t = gmtime(&mtime); 627169689Skan reply(213, 628169689Skan "%04d%02d%02d%02d%02d%02d", 629169689Skan t->tm_year + 1900, 630169689Skan t->tm_mon + 1, 631169689Skan t->tm_mday, 632169689Skan t->tm_hour, 633169689Skan t->tm_min, 634169689Skan t->tm_sec); 635169689Skan } 636169689Skan } 637169689Skan if ($3 != NULL) 638169689Skan free($3); 639169689Skan } 640169689Skan | QUIT CRLF check_secure 641169689Skan { 642169689Skan if ($3) { 643169689Skan reply(221, "Goodbye."); 644169689Skan dologout(0); 645169689Skan } 646169689Skan } 647169689Skan | error CRLF 648169689Skan { 649169689Skan yyerrok; 650169689Skan } 651169689Skan ; 652169689Skanrcmd 653169689Skan : RNFR SP pathname CRLF check_login_no_guest 654169689Skan { 655169689Skan restart_point = (off_t) 0; 656169689Skan if ($5 && $3) { 657169689Skan fromname = renamefrom($3); 658169689Skan if (fromname == (char *) 0 && $3) { 659169689Skan free($3); 660169689Skan } 661169689Skan } 662169689Skan } 663169689Skan | REST SP byte_size CRLF check_secure 664169689Skan { 665169689Skan if ($5) { 666169689Skan fromname = (char *) 0; 667169689Skan restart_point = $3; /* XXX $3 is only "int" */ 668169689Skan reply(350, "Restarting at %ld. %s", 669169689Skan (long)restart_point, 670169689Skan "Send STORE or RETRIEVE to initiate transfer."); 671169689Skan } 672169689Skan } 673169689Skan | AUTH SP STRING CRLF 674169689Skan { 675169689Skan auth($3); 676132718Skan free($3); 677132718Skan } 678132718Skan | ADAT SP STRING CRLF 679132718Skan { 68090075Sobrien adat($3); 68190075Sobrien free($3); 68290075Sobrien } 683169689Skan | PBSZ SP NUMBER CRLF check_secure 684169689Skan { 685169689Skan if ($5) 686169689Skan pbsz($3); 687169689Skan } 688169689Skan | PROT SP STRING CRLF check_secure 689169689Skan { 690169689Skan if ($5) 691169689Skan prot($3); 692169689Skan } 693169689Skan | CCC CRLF check_secure 694169689Skan { 695169689Skan if ($3) 696169689Skan ccc(); 697169689Skan } 698169689Skan | MIC SP STRING CRLF 699169689Skan { 700169689Skan mec($3, prot_safe); 701169689Skan free($3); 702169689Skan } 703169689Skan | CONF SP STRING CRLF 704169689Skan { 705169689Skan mec($3, prot_confidential); 706169689Skan free($3); 707169689Skan } 708169689Skan | ENC SP STRING CRLF 709169689Skan { 710169689Skan mec($3, prot_private); 71190075Sobrien free($3); 712132718Skan } 713132718Skan ; 714132718Skan 715132718Skanusername 716169689Skan : STRING 71790075Sobrien ; 71890075Sobrien 71990075Sobrienpassword 720169689Skan : /* empty */ 721169689Skan { 722169689Skan $$ = (char *)calloc(1, sizeof(char)); 723169689Skan } 724169689Skan | STRING 725169689Skan ; 726169689Skan 727169689Skanbyte_size 728132718Skan : NUMBER 729132718Skan ; 730132718Skan 731117395Skanhost_port 732169689Skan : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 733169689Skan NUMBER COMMA NUMBER 734117395Skan { 735117395Skan struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest; 736117395Skan 737117395Skan sin4->sin_family = AF_INET; 738117395Skan sin4->sin_port = htons($9 * 256 + $11); 739169689Skan sin4->sin_addr.s_addr = 740169689Skan htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7); 741169689Skan } 742169689Skan ; 743169689Skan 744169689Skanform_code 745169689Skan : N 746169689Skan { 747169689Skan $$ = FORM_N; 748169689Skan } 749169689Skan | T 750169689Skan { 751169689Skan $$ = FORM_T; 752169689Skan } 753169689Skan | C 754169689Skan { 755169689Skan $$ = FORM_C; 756169689Skan } 757169689Skan ; 758169689Skan 759169689Skantype_code 760169689Skan : A 761132718Skan { 762132718Skan cmd_type = TYPE_A; 763132718Skan cmd_form = FORM_N; 764132718Skan } 765132718Skan | A SP form_code 766132718Skan { 767132718Skan cmd_type = TYPE_A; 768132718Skan cmd_form = $3; 769132718Skan } 770132718Skan | E 771132718Skan { 772132718Skan cmd_type = TYPE_E; 773132718Skan cmd_form = FORM_N; 774169689Skan } 775132718Skan | E SP form_code 776132718Skan { 777132718Skan cmd_type = TYPE_E; 778132718Skan cmd_form = $3; 779132718Skan } 780132718Skan | I 781132718Skan { 782132718Skan cmd_type = TYPE_I; 783132718Skan } 784132718Skan | L 785169689Skan { 786132718Skan cmd_type = TYPE_L; 787132718Skan cmd_bytesz = NBBY; 788132718Skan } 789132718Skan | L SP byte_size 790132718Skan { 791169689Skan cmd_type = TYPE_L; 792169689Skan cmd_bytesz = $3; 793169689Skan } 794169689Skan /* this is for a bug in the BBN ftp */ 795169689Skan | L byte_size 796169689Skan { 797169689Skan cmd_type = TYPE_L; 798169689Skan cmd_bytesz = $2; 799169689Skan } 800169689Skan ; 801132718Skan 80290075Sobrienstruct_code 803169689Skan : F 804169689Skan { 805169689Skan $$ = STRU_F; 806169689Skan } 807169689Skan | R 808169689Skan { 809169689Skan $$ = STRU_R; 810132718Skan } 811132718Skan | P 812117395Skan { 813169689Skan $$ = STRU_P; 814169689Skan } 815169689Skan ; 816132718Skan 817132718Skanmode_code 818132718Skan : S 819169689Skan { 820169689Skan $$ = MODE_S; 821169689Skan } 822169689Skan | B 823169689Skan { 824169689Skan $$ = MODE_B; 825169689Skan } 826169689Skan | C 827169689Skan { 828169689Skan $$ = MODE_C; 829132718Skan } 83090075Sobrien ; 83190075Sobrien 832169689Skanpathname 833169689Skan : pathstring 834169689Skan { 835169689Skan /* 836169689Skan * Problem: this production is used for all pathname 837169689Skan * processing, but only gives a 550 error reply. 838169689Skan * This is a valid reply in some cases but not in others. 839169689Skan */ 840169689Skan if (logged_in && $1 && *$1 == '~') { 841169689Skan glob_t gl; 842169689Skan int flags = 843169689Skan GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 844169689Skan 845169689Skan memset(&gl, 0, sizeof(gl)); 846169689Skan if (glob($1, flags, NULL, &gl) || 847223262Sbenl gl.gl_pathc == 0) { 848223262Sbenl reply(550, "not found"); 849223262Sbenl $$ = NULL; 850132718Skan } else { 851169689Skan $$ = strdup(gl.gl_pathv[0]); 852223262Sbenl } 853169689Skan globfree(&gl); 854169689Skan free($1); 85590075Sobrien } else 85650397Sobrien $$ = $1; 85750397Sobrien } 858169689Skan ; 859169689Skan 86050397Sobrienpathstring 86150397Sobrien : STRING 862169689Skan ; 863169689Skan 864169689Skanoctal_number 865169689Skan : NUMBER 866169689Skan { 867169689Skan int ret, dec, multby, digit; 868169689Skan 869169689Skan /* 870169689Skan * Convert a number that was read as decimal number 871169689Skan * to what it would be if it had been read as octal. 872261188Spfg */ 873261188Spfg dec = $1; 87450397Sobrien multby = 1; 875169689Skan ret = 0; 876169689Skan while (dec) { 877169689Skan digit = dec%10; 878169689Skan if (digit > 7) { 879169689Skan ret = -1; 880169689Skan break; 881169689Skan } 882169689Skan ret += digit * multby; 88318334Speter multby *= 8; 88418334Speter dec /= 10; 88590075Sobrien } 88690075Sobrien $$ = ret; 88718334Speter } 88818334Speter ; 88990075Sobrien 89090075Sobrien 891169689Skancheck_login_no_guest : check_login 89290075Sobrien { 893169689Skan $$ = $1 && !guest; 894169689Skan if($1 && !$$) 895169689Skan reply(550, "Permission denied"); 896169689Skan } 89718334Speter ; 89818334Speter 89918334Spetercheck_login : check_secure 90018334Speter { 90118334Speter if($1) { 90218334Speter if(($$ = logged_in) == 0) 90318334Speter reply(530, "Please login with USER and PASS."); 90418334Speter } else 90518334Speter $$ = 0; 90618334Speter } 90718334Speter ; 90818334Speter 90918334Spetercheck_secure : /* empty */ 91018334Speter { 91190075Sobrien $$ = 1; 91218334Speter if(sec_complete && !ccc_passed && !secure_command()) { 91318334Speter $$ = 0; 91418334Speter reply(533, "Command protection level denied " 91590075Sobrien "for paranoid reasons."); 91618334Speter } 91718334Speter } 91890075Sobrien ; 91918334Speter 92090075Sobrien%% 92190075Sobrien 92290075Sobrien#define CMD 0 /* beginning of command */ 92390075Sobrien#define ARGS 1 /* expect miscellaneous arguments */ 92490075Sobrien#define STR1 2 /* expect SP followed by STRING */ 92590075Sobrien#define STR2 3 /* expect STRING */ 92690075Sobrien#define OSTR 4 /* optional SP then STRING */ 92790075Sobrien#define ZSTR1 5 /* SP then optional STRING */ 92890075Sobrien#define ZSTR2 6 /* optional STRING after SP */ 929169689Skan#define SITECMD 7 /* SITE command */ 930169689Skan#define NSTR 8 /* Number followed by a string */ 93190075Sobrien 93290075Sobrienstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 93318334Speter { "USER", USER, STR1, 1, "<sp> username" }, 93418334Speter { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 93518334Speter { "ACCT", ACCT, STR1, 0, "(specify account)" }, 93618334Speter { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 93718334Speter { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 93818334Speter { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 93990075Sobrien { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 94018334Speter { "EPRT", EPRT, STR1, 1, "<sp> string" }, 94118334Speter { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 94290075Sobrien { "EPSV", EPSV, OSTR, 1, "[<sp> foo]" }, 94318334Speter { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 944169689Skan { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 945169689Skan { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 946169689Skan { "RETR", RETR, STR1, 1, "<sp> file-name" }, 947169689Skan { "STOR", STOR, STR1, 1, "<sp> file-name" }, 948169689Skan { "APPE", APPE, STR1, 1, "<sp> file-name" }, 949169689Skan { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 950169689Skan { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 95118334Speter { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 952169689Skan { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 953169689Skan { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 95418334Speter { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 95518334Speter { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 956169689Skan { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 957169689Skan { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 958169689Skan { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 95918334Speter { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 960132718Skan { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 961132718Skan { "DELE", DELE, STR1, 1, "<sp> file-name" }, 962132718Skan { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 963132718Skan { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 964132718Skan { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 965132718Skan { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 966132718Skan { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 967132718Skan { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 968132718Skan { "STAT", sTAT, OSTR, 1, "[ <sp> path-name ]" }, 969132718Skan { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 970169689Skan { "NOOP", NOOP, ARGS, 1, "" }, 971169689Skan { "MKD", MKD, STR1, 1, "<sp> path-name" }, 972169689Skan { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 973169689Skan { "RMD", RMD, STR1, 1, "<sp> path-name" }, 974169689Skan { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 975169689Skan { "PWD", PWD, ARGS, 1, "(return current directory)" }, 97618334Speter { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 977169689Skan { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 978169689Skan { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 97918334Speter { "STOU", STOU, STR1, 1, "<sp> file-name" }, 980169689Skan { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 981169689Skan { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 982169689Skan 983169689Skan /* extensions from RFC2228 */ 984169689Skan { "AUTH", AUTH, STR1, 1, "<sp> auth-type" }, 98518334Speter { "ADAT", ADAT, STR1, 1, "<sp> auth-data" }, 986169689Skan { "PBSZ", PBSZ, ARGS, 1, "<sp> buffer-size" }, 987169689Skan { "PROT", PROT, STR1, 1, "<sp> prot-level" }, 988169689Skan { "CCC", CCC, ARGS, 1, "" }, 989169689Skan { "MIC", MIC, STR1, 1, "<sp> integrity command" }, 99018334Speter { "CONF", CONF, STR1, 1, "<sp> confidentiality command" }, 991169689Skan { "ENC", ENC, STR1, 1, "<sp> privacy command" }, 992169689Skan 993169689Skan /* RFC2389 */ 99418334Speter { "FEAT", FEAT, ARGS, 1, "" }, 99518334Speter { "OPTS", OPTS, ARGS, 1, "<sp> command [<sp> options]" }, 996169689Skan 99718334Speter { NULL, 0, 0, 0, 0 } 998132718Skan}; 999169689Skan 1000169689Skanstruct tab sitetab[] = { 100118334Speter { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1002261188Spfg { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 100318334Speter { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1004261188Spfg { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1005261188Spfg 1006261188Spfg { "KAUTH", KAUTH, STR1, 1, "<sp> principal [ <sp> ticket ]" }, 1007261188Spfg { "KLIST", KLIST, ARGS, 1, "(show ticket file)" }, 100850397Sobrien { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" }, 100990075Sobrien { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" }, 101090075Sobrien { "AFSLOG", AFSLOG, OSTR, 1, "[<sp> cell]" }, 101190075Sobrien 101290075Sobrien { "LOCATE", LOCATE, STR1, 1, "<sp> globexpr" }, 101390075Sobrien { "FIND", LOCATE, STR1, 1, "<sp> globexpr" }, 101490075Sobrien 101590075Sobrien { "URL", URL, ARGS, 1, "?" }, 101690075Sobrien 101790075Sobrien { NULL, 0, 0, 0, 0 } 101890075Sobrien}; 101990075Sobrien 102090075Sobrienstatic struct tab * 102190075Sobrienlookup(struct tab *p, char *cmd) 102290075Sobrien{ 102318334Speter 102418334Speter for (; p->name != NULL; p++) 102518334Speter if (strcmp(cmd, p->name) == 0) 102618334Speter return (p); 102718334Speter return (0); 102818334Speter} 102918334Speter 103090075Sobrien/* 103190075Sobrien * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes. 103290075Sobrien */ 103318334Speterchar * 1034117395Skanftpd_getline(char *s, int n) 103518334Speter{ 103618334Speter int c; 103718334Speter char *cs; 103818334Speter 103918334Speter cs = s; 104018334Speter 104118334Speter /* might still be data within the security MIC/CONF/ENC */ 104218334Speter if(ftp_command){ 1043169689Skan strlcpy(s, ftp_command, n); 1044169689Skan if (debug) 1045169689Skan syslog(LOG_DEBUG, "command: %s", s); 1046169689Skan return s; 1047169689Skan } 1048169689Skan while ((c = getc(stdin)) != EOF) { 1049169689Skan c &= 0377; 1050169689Skan if (c == IAC) { 1051169689Skan if ((c = getc(stdin)) != EOF) { 1052169689Skan c &= 0377; 105318334Speter switch (c) { 105418334Speter case WILL: 1055169689Skan case WONT: 1056169689Skan c = getc(stdin); 1057169689Skan printf("%c%c%c", IAC, DONT, 0377&c); 1058169689Skan fflush(stdout); 105918334Speter continue; 106018334Speter case DO: 1061169689Skan case DONT: 1062169689Skan c = getc(stdin); 1063169689Skan printf("%c%c%c", IAC, WONT, 0377&c); 106496263Sobrien fflush(stdout); 106596263Sobrien continue; 1066169689Skan case IAC: 1067169689Skan break; 1068169689Skan default: 1069169689Skan continue; /* ignore command */ 107018334Speter } 1071169689Skan } 1072169689Skan } 1073169689Skan *cs++ = c; 1074169689Skan if (--n <= 0 || c == '\n') 1075169689Skan break; 1076169689Skan } 107718334Speter if (c == EOF && cs == s) 107896263Sobrien return (NULL); 107996263Sobrien *cs++ = '\0'; 108096263Sobrien if (debug) { 108196263Sobrien if (!guest && strncasecmp("pass ", s, 5) == 0) { 1082169689Skan /* Don't syslog passwords */ 108318334Speter syslog(LOG_DEBUG, "command: %.5s ???", s); 108418334Speter } else { 108518334Speter char *cp; 108690075Sobrien int len; 108790075Sobrien 108818334Speter /* Don't syslog trailing CR-LF */ 1089169689Skan len = strlen(s); 1090169689Skan cp = s + len - 1; 1091169689Skan while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1092169689Skan --cp; 1093169689Skan --len; 109496263Sobrien } 109596263Sobrien syslog(LOG_DEBUG, "command: %.*s", len, s); 1096169689Skan } 1097169689Skan } 109818334Speter#ifdef XXX 1099169689Skan fprintf(stderr, "%s\n", s); 1100169689Skan#endif 1101259268Spfg return (s); 1102259268Spfg} 1103259268Spfg 1104259268Spfgstatic RETSIGTYPE 1105259268Spfgtoolong(int signo) 1106169689Skan{ 110718334Speter 110890075Sobrien reply(421, 110918334Speter "Timeout (%d seconds): closing control connection.", 111018334Speter ftpd_timeout); 111118334Speter if (logging) 111218334Speter syslog(LOG_INFO, "User %s timed out after %d seconds", 1113169689Skan (pw ? pw -> pw_name : "unknown"), ftpd_timeout); 1114169689Skan dologout(1); 1115169689Skan SIGRETURN(0); 111618334Speter} 1117169689Skan 1118169689Skanstatic int 1119169689Skanyylex(void) 1120169689Skan{ 1121169689Skan static int cpos, state; 1122169689Skan char *cp, *cp2; 1123169689Skan struct tab *p; 1124169689Skan int n; 1125169689Skan char c; 1126169689Skan 1127169689Skan for (;;) { 1128169689Skan switch (state) { 1129169689Skan 1130169689Skan case CMD: 1131169689Skan hasyyerrored = 0; 1132169689Skan 1133169689Skan signal(SIGALRM, toolong); 1134169689Skan alarm((unsigned) ftpd_timeout); 113518334Speter if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) { 113618334Speter reply(221, "You could at least say goodbye."); 113718334Speter dologout(0); 113818334Speter } 1139169689Skan alarm(0); 1140169689Skan#ifdef HAVE_SETPROCTITLE 1141169689Skan if (strncasecmp(cbuf, "PASS", 4) != 0) 114218334Speter setproctitle("%s: %s", proctitle, cbuf); 114318334Speter#endif /* HAVE_SETPROCTITLE */ 114418334Speter if ((cp = strchr(cbuf, '\r'))) { 114518334Speter *cp++ = '\n'; 114618334Speter *cp = '\0'; 114718334Speter } 114818334Speter if ((cp = strpbrk(cbuf, " \n"))) 114918334Speter cpos = cp - cbuf; 1150169689Skan if (cpos == 0) 1151169689Skan cpos = 4; 1152169689Skan c = cbuf[cpos]; 1153169689Skan cbuf[cpos] = '\0'; 1154169689Skan strupr(cbuf); 1155169689Skan p = lookup(cmdtab, cbuf); 1156169689Skan cbuf[cpos] = c; 1157169689Skan if (p != 0) { 1158169689Skan if (p->implemented == 0) { 1159169689Skan nack(p->name); 1160169689Skan hasyyerrored = 1; 1161169689Skan break; 116218334Speter } 1163169689Skan state = p->state; 1164169689Skan yylval.s = p->name; 116518334Speter return (p->token); 1166132718Skan } 1167169689Skan break; 1168169689Skan 116990075Sobrien case SITECMD: 1170169689Skan if (cbuf[cpos] == ' ') { 1171169689Skan cpos++; 1172169689Skan return (SP); 117318334Speter } 1174169689Skan cp = &cbuf[cpos]; 1175169689Skan if ((cp2 = strpbrk(cp, " \n"))) 117618334Speter cpos = cp2 - cbuf; 1177169689Skan c = cbuf[cpos]; 1178169689Skan cbuf[cpos] = '\0'; 117990075Sobrien strupr(cp); 1180169689Skan p = lookup(sitetab, cp); 1181169689Skan cbuf[cpos] = c; 1182169689Skan if (p != 0) { 1183169689Skan if (p->implemented == 0) { 1184169689Skan state = CMD; 1185169689Skan nack(p->name); 1186169689Skan hasyyerrored = 1; 118718334Speter break; 118818334Speter } 118918334Speter state = p->state; 119018334Speter yylval.s = p->name; 119118334Speter return (p->token); 119218334Speter } 1193169689Skan state = CMD; 1194169689Skan break; 1195169689Skan 119618334Speter case OSTR: 119718334Speter if (cbuf[cpos] == '\n') { 119818334Speter state = CMD; 119918334Speter return (CRLF); 120018334Speter } 1201169689Skan /* FALLTHROUGH */ 1202169689Skan 120318334Speter case STR1: 120418334Speter case ZSTR1: 120590075Sobrien dostr1: 120690075Sobrien if (cbuf[cpos] == ' ') { 120790075Sobrien cpos++; 120818334Speter if(state == OSTR) 1209169689Skan state = STR2; 1210169689Skan else 1211169689Skan state++; 1212132718Skan return (SP); 1213169689Skan } 1214169689Skan break; 1215169689Skan 1216169689Skan case ZSTR2: 1217132718Skan if (cbuf[cpos] == '\n') { 1218132718Skan state = CMD; 1219169689Skan return (CRLF); 1220132718Skan } 122190075Sobrien /* FALLTHROUGH */ 122290075Sobrien 122390075Sobrien case STR2: 122490075Sobrien cp = &cbuf[cpos]; 122590075Sobrien n = strlen(cp); 1226169689Skan cpos += n - 1; 1227169689Skan /* 1228169689Skan * Make sure the string is nonempty and \n terminated. 1229169689Skan */ 123090075Sobrien if (n > 1 && cbuf[cpos] == '\n') { 123190075Sobrien cbuf[cpos] = '\0'; 123218334Speter yylval.s = copy(cp); 123318334Speter cbuf[cpos] = '\n'; 123418334Speter state = ARGS; 123518334Speter return (STRING); 123618334Speter } 123718334Speter break; 1238169689Skan 123990075Sobrien case NSTR: 1240169689Skan if (cbuf[cpos] == ' ') { 1241169689Skan cpos++; 124290075Sobrien return (SP); 1243260918Spfg } 1244260918Spfg if (isdigit((unsigned char)cbuf[cpos])) { 1245260918Spfg cp = &cbuf[cpos]; 1246260918Spfg while (isdigit((unsigned char)cbuf[++cpos])) 1247260918Spfg ; 1248260918Spfg c = cbuf[cpos]; 1249169689Skan cbuf[cpos] = '\0'; 1250169689Skan yylval.i = atoi(cp); 1251169689Skan cbuf[cpos] = c; 1252169689Skan state = STR1; 1253169689Skan return (NUMBER); 1254169689Skan } 1255169689Skan state = STR1; 1256169689Skan goto dostr1; 1257169689Skan 1258169689Skan case ARGS: 1259169689Skan if (isdigit((unsigned char)cbuf[cpos])) { 126018334Speter cp = &cbuf[cpos]; 126118334Speter while (isdigit((unsigned char)cbuf[++cpos])) 126218334Speter ; 126318334Speter c = cbuf[cpos]; 126418334Speter cbuf[cpos] = '\0'; 126518334Speter yylval.i = atoi(cp); 126618334Speter cbuf[cpos] = c; 126718334Speter return (NUMBER); 126818334Speter } 126918334Speter switch (cbuf[cpos++]) { 127018334Speter 127118334Speter case '\n': 127218334Speter state = CMD; 127318334Speter return (CRLF); 127418334Speter 127590075Sobrien case ' ': 127690075Sobrien return (SP); 127790075Sobrien 127818334Speter case ',': 127990075Sobrien return (COMMA); 128090075Sobrien 128190075Sobrien case 'A': 128290075Sobrien case 'a': 128318334Speter return (A); 128490075Sobrien 128590075Sobrien case 'B': 128690075Sobrien case 'b': 128790075Sobrien return (B); 1288117395Skan 128990075Sobrien case 'C': 1290132718Skan case 'c': 1291117395Skan return (C); 129218334Speter 129390075Sobrien case 'E': 1294169689Skan case 'e': 129518334Speter return (E); 129618334Speter 1297117395Skan case 'F': 1298117395Skan case 'f': 1299117395Skan return (F); 1300117395Skan 130118334Speter case 'I': 1302117395Skan case 'i': 1303117395Skan return (I); 130490075Sobrien 1305117395Skan case 'L': 130618334Speter case 'l': 130790075Sobrien return (L); 1308117395Skan 130918334Speter case 'N': 131018334Speter case 'n': 131118334Speter return (N); 131250397Sobrien 1313169689Skan case 'P': 1314169689Skan case 'p': 131518334Speter return (P); 1316117395Skan 131718334Speter case 'R': 131890075Sobrien case 'r': 131918334Speter return (R); 1320169689Skan 132118334Speter case 'S': 132218334Speter case 's': 132318334Speter return (S); 132450397Sobrien 132550397Sobrien case 'T': 132618334Speter case 't': 1327117395Skan return (T); 132818334Speter 132990075Sobrien } 133090075Sobrien break; 133190075Sobrien 133218334Speter default: 133396263Sobrien fatal("Unknown state in scanner."); 133496263Sobrien } 133596263Sobrien yyerror(NULL); 133696263Sobrien state = CMD; 1337117395Skan return (0); 133896263Sobrien } 133996263Sobrien} 134096263Sobrien 134196263Sobrien/* ARGSUSED */ 134218334Spetervoid 1343169689Skanyyerror(char *s) 134490075Sobrien{ 134518334Speter char *cp; 134618334Speter 134790075Sobrien if (hasyyerrored) 134890075Sobrien return; 134990075Sobrien 135090075Sobrien if ((cp = strchr(cbuf,'\n'))) 1351117395Skan *cp = '\0'; 1352117395Skan reply(500, "'%s': command not understood.", cbuf); 135318334Speter hasyyerrored = 1; 135490075Sobrien} 135590075Sobrien 135690075Sobrienstatic char * 135790075Sobriencopy(char *s) 135890075Sobrien{ 135990075Sobrien char *p; 136090075Sobrien 1361117395Skan p = strdup(s); 136218334Speter if (p == NULL) 136390075Sobrien fatal("Ran out of memory."); 136490075Sobrien return p; 136518334Speter} 136618334Speter 136718334Speterstatic void 136850397Sobrienhelp(struct tab *ctab, char *s) 136950397Sobrien{ 137018334Speter struct tab *c; 1371117395Skan int width, NCMDS; 137218334Speter char *t; 137390075Sobrien char buf[1024]; 137490075Sobrien 137590075Sobrien if (ctab == sitetab) 137618334Speter t = "SITE "; 137718334Speter else 137818334Speter t = ""; 137950397Sobrien width = 0, NCMDS = 0; 138090075Sobrien for (c = ctab; c->name != NULL; c++) { 138190075Sobrien int len = strlen(c->name); 138218334Speter 1383117395Skan if (len > width) 1384117395Skan width = len; 1385117395Skan NCMDS++; 138618334Speter } 138790075Sobrien width = (width + 8) &~ 7; 138818334Speter if (s == 0) { 1389117395Skan int i, j, w; 139018334Speter int columns, lines; 139118334Speter 1392169689Skan lreply(214, "The following %scommands are recognized %s.", 1393169689Skan t, "(* =>'s unimplemented)"); 139418334Speter columns = 76 / width; 1395169689Skan if (columns == 0) 1396169689Skan columns = 1; 1397169689Skan lines = (NCMDS + columns - 1) / columns; 1398169689Skan for (i = 0; i < lines; i++) { 1399169689Skan strlcpy (buf, " ", sizeof(buf)); 1400169689Skan for (j = 0; j < columns; j++) { 1401169689Skan c = ctab + j * lines + i; 1402169689Skan snprintf (buf + strlen(buf), 1403169689Skan sizeof(buf) - strlen(buf), 1404132718Skan "%s%c", 1405169689Skan c->name, 1406169689Skan c->implemented ? ' ' : '*'); 1407169689Skan if (c + lines >= &ctab[NCMDS]) 1408169689Skan break; 1409169689Skan w = strlen(c->name) + 1; 1410169689Skan while (w < width) { 1411169689Skan strlcat (buf, 1412169689Skan " ", 1413169689Skan sizeof(buf)); 1414169689Skan w++; 141518334Speter } 1416169689Skan } 1417169689Skan lreply(214, "%s", buf); 1418169689Skan } 1419169689Skan reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se"); 1420169689Skan return; 1421169689Skan } 1422169689Skan strupr(s); 142318334Speter c = lookup(ctab, s); 1424169689Skan if (c == (struct tab *)0) { 1425169689Skan reply(502, "Unknown command %s.", s); 1426169689Skan return; 1427169689Skan } 1428169689Skan if (c->implemented) 1429169689Skan reply(214, "Syntax: %s%s %s", t, c->name, c->help); 1430169689Skan else 1431169689Skan reply(214, "%s%-*s\t%s; unimplemented.", t, width, 1432169689Skan c->name, c->help); 143318334Speter} 1434169689Skan 1435169689Skanstatic void 143618334Spetersizecmd(char *filename) 1437169689Skan{ 1438169689Skan switch (type) { 1439169689Skan case TYPE_L: 1440169689Skan case TYPE_I: { 1441169689Skan struct stat stbuf; 1442169689Skan if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1443169689Skan reply(550, "%s: not a plain file.", filename); 1444169689Skan else 1445169689Skan reply(213, "%lu", (unsigned long)stbuf.st_size); 1446169689Skan break; 1447169689Skan } 1448169689Skan case TYPE_A: { 1449169689Skan FILE *fin; 145018334Speter int c; 1451132718Skan size_t count; 145250397Sobrien struct stat stbuf; 145318334Speter fin = fopen(filename, "r"); 145452284Sobrien if (fin == NULL) { 1455132718Skan perror_reply(550, filename); 145652284Sobrien return; 1457169689Skan } 1458169689Skan if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1459169689Skan reply(550, "%s: not a plain file.", filename); 1460169689Skan fclose(fin); 1461169689Skan return; 1462169689Skan } 1463169689Skan 1464169689Skan count = 0; 1465169689Skan while((c=getc(fin)) != EOF) { 1466169689Skan if (c == '\n') /* will get expanded to \r\n */ 1467169689Skan count++; 1468169689Skan count++; 1469169689Skan } 1470169689Skan fclose(fin); 1471169689Skan 1472169689Skan reply(213, "%lu", (unsigned long)count); 1473169689Skan break; 1474169689Skan } 1475169689Skan default: 1476169689Skan reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1477169689Skan } 1478169689Skan} 1479169689Skan