ftpcmd.y revision 22347
1187770Sluigi/* ftpcmd.y: yacc parser for the FTP daemon. 2187770Sluigi 3187770Sluigi%%% portions-copyright-cmetz 4187770SluigiPortions of this software are Copyright 1996 by Craig Metz, All Rights 5187770SluigiReserved. The Inner Net License Version 2 applies to these portions of 6187770Sluigithe software. 7187770SluigiYou should have received a copy of the license with this software. If 8187770Sluigiyou didn't get a copy, you may request one from <license@inner.net>. 9187770Sluigi 10187770Sluigi History: 11187770Sluigi 12187770Sluigi Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here. 13187770Sluigi Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings. 14187770Sluigi Use FUNCTION declaration et al. Removed useless strings. 15187770Sluigi Changed some char []s to char *s. Deleted comment address. 16187770Sluigi Changed tmpline references to be more pure-pointer 17187770Sluigi references. Changed tmpline declaration back to char []. 18187770Sluigi Modified at NRL for OPIE 2.1. Minor changes for autoconf. 19187770Sluigi Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[] 20187770Sluigi -- fixes problems experienced by bison users. Merged in new 21187770Sluigi PORT attack fixes from Hobbit. 22187770Sluigi Modified at NRL for OPIE 2.0. 23187770Sluigi Originally from BSD. 24187770Sluigi*/ 25187770Sluigi/* 26187770Sluigi * Copyright (c) 1985, 1988 Regents of the University of California. 27187770Sluigi * All rights reserved. 28187770Sluigi * 29187770Sluigi * Redistribution and use in source and binary forms, with or without 30187770Sluigi * modification, are permitted provided that the following conditions 31187770Sluigi * are met: 32187770Sluigi * 1. Redistributions of source code must retain the above copyright 33187770Sluigi * notice, this list of conditions and the following disclaimer. 34187770Sluigi * 2. Redistributions in binary form must reproduce the above copyright 35187770Sluigi * notice, this list of conditions and the following disclaimer in the 36187770Sluigi * documentation and/or other materials provided with the distribution. 37187770Sluigi * 3. All advertising materials mentioning features or use of this software 38187770Sluigi * must display the following acknowledgement: 39187770Sluigi * This product includes software developed by the University of 40187770Sluigi * California, Berkeley and its contributors. 41187770Sluigi * 4. Neither the name of the University nor the names of its contributors 42187770Sluigi * may be used to endorse or promote products derived from this software 43187770Sluigi * without specific prior written permission. 44187770Sluigi * 45187770Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 46187770Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47187770Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48187770Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 49187770Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50220802Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51220802Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52220802Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53220802Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54220802Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55220802Sglebius * SUCH DAMAGE. 56223080Sae * 57220802Sglebius * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91 58220804Sglebius */ 59220802Sglebius 60187770Sluigi/* 61187770Sluigi * Grammar for FTP commands. 62187770Sluigi * See RFC 959. 63187770Sluigi */ 64187770Sluigi 65187770Sluigi%{ 66187770Sluigi#include "opie_cfg.h" 67220802Sglebius 68187770Sluigi#include <sys/param.h> 69187770Sluigi#include <sys/types.h> 70220802Sglebius#include <sys/socket.h> 71187770Sluigi#include <sys/stat.h> 72187770Sluigi#include <netinet/in.h> 73187770Sluigi#include <arpa/ftp.h> 74187770Sluigi#include <signal.h> 75187770Sluigi#include <setjmp.h> 76187770Sluigi#include <syslog.h> 77187770Sluigi#if TM_IN_SYS_TIME 78187770Sluigi#include <sys/time.h> 79187770Sluigi#else /* TM_IN_SYS_TIME */ 80187770Sluigi#include <time.h> 81187770Sluigi#endif /* TM_IN_SYS_TIME */ 82187770Sluigi#include <pwd.h> 83187770Sluigi#include <unistd.h> 84187770Sluigi#include <stdio.h> 85187770Sluigi#include <ctype.h> 86187770Sluigi#include <stdlib.h> 87187770Sluigi#include <string.h> 88220804Sglebius 89187770Sluigi#include "opie.h" 90220804Sglebius 91187770Sluigi#if HAVE_LS_G_FLAG 92187770Sluigi#define LS_COMMAND "/bin/ls -lgA" 93187770Sluigi#else /* HAVE_LS_G_FLAG */ 94187770Sluigi#define LS_COMMAND "/bin/ls -lA" 95187770Sluigi#endif /* HAVE_LS_G_FLAG */ 96187770Sluigi 97187770Sluigiextern struct sockaddr_in data_dest; 98187770Sluigiextern struct sockaddr_in his_addr; 99187770Sluigiextern int logged_in; 100187770Sluigiextern struct passwd *pw; 101187770Sluigiextern int guest; 102187770Sluigiextern int type; 103187770Sluigiextern int form; 104187770Sluigiextern int debug; 105187770Sluigiextern int timeout; 106187770Sluigiextern int maxtimeout; 107187770Sluigiextern int pdata; 108187770Sluigiextern char *remotehost; 109187770Sluigiextern char *proctitle; 110187770Sluigiextern char *globerr; 111187770Sluigiextern int usedefault; 112187770Sluigiextern int transflag; 113187770Sluigiextern char tmpline[]; 114187770Sluigichar **ftpglob(); 115187770Sluigi 116187770SluigiVOIDRET dologout __P((int)); 117187770SluigiVOIDRET upper __P((char *)); 118187770SluigiVOIDRET nack __P((char *)); 119187770SluigiVOIDRET opiefatal __P((char *)); 120187770Sluigi 121187770SluigiVOIDRET pass __P((char *)); 122187770Sluigiint user __P((char *)); 123187770SluigiVOIDRET passive __P((void)); 124187770SluigiVOIDRET retrieve __P((char *, char *)); 125187770SluigiVOIDRET store __P((char *, char *, int)); 126187770SluigiVOIDRET send_file_list __P((char *)); 127187770SluigiVOIDRET statfilecmd __P((char *)); 128187770SluigiVOIDRET statcmd __P((void)); 129187770SluigiVOIDRET delete __P((char *)); 130187770SluigiVOIDRET renamecmd __P((char *, char *)); 131187770SluigiVOIDRET cwd __P((char *)); 132187770SluigiVOIDRET makedir __P((char *)); 133187770SluigiVOIDRET removedir __P((char *)); 134187770SluigiVOIDRET pwd __P((void)); 135187770Sluigi 136187770SluigiVOIDRET sizecmd __P((char *)); 137187770Sluigi 138187770Sluigioff_t restart_point; 139187770Sluigi 140187770Sluigistatic int cmd_type; 141187770Sluigistatic int cmd_form; 142187770Sluigistatic int cmd_bytesz; 143187770Sluigistatic unsigned short cliport = 0; 144187770Sluigichar cbuf[512]; 145187770Sluigichar *fromname; 146187770Sluigi 147187770Sluigistruct tab { 148187770Sluigi char *name; 149187770Sluigi short token; 150187770Sluigi short state; 151187770Sluigi short implemented; /* 1 if command is implemented */ 152187770Sluigi char *help; 153187770Sluigi}; 154187770Sluigi 155187770SluigiVOIDRET help __P((struct tab *, char *)); 156187770Sluigi 157187770Sluigistruct tab cmdtab[], sitetab[]; 158187770Sluigi 159187770Sluigi%} 160187770Sluigi 161187770Sluigi%token 162187770Sluigi A B C E F I 163187770Sluigi L N P R S T 164187770Sluigi 165187770Sluigi SP CRLF COMMA STRING NUMBER 166187770Sluigi 167220802Sglebius USER PASS ACCT REIN QUIT PORT 168187770Sluigi PASV TYPE STRU MODE RETR STOR 169220802Sglebius APPE MLFL MAIL MSND MSOM MSAM 170220802Sglebius MRSQ MRCP ALLO REST RNFR RNTO 171187770Sluigi ABOR DELE CWD LIST NLST SITE 172187770Sluigi STAT HELP NOOP MKD RMD PWD 173187770Sluigi CDUP STOU SMNT SYST SIZE MDTM 174187770Sluigi 175187770Sluigi UMASK IDLE CHMOD 176187770Sluigi 177220802Sglebius LEXERR 178187770Sluigi 179187770Sluigi%start cmd_list 180187770Sluigi 181187770Sluigi%% 182187770Sluigi 183220802Sglebiuscmd_list: /* empty */ 184220802Sglebius | cmd_list cmd 185220802Sglebius = { 186187770Sluigi fromname = (char *) 0; 187187770Sluigi restart_point = (off_t) 0; 188187770Sluigi } 189187770Sluigi | cmd_list rcmd 190187770Sluigi ; 191187770Sluigi 192187770Sluigicmd: USER SP username CRLF 193220802Sglebius = { 194187770Sluigi user((char *) $3); 195187770Sluigi free((char *) $3); 196187770Sluigi } 197187770Sluigi | PASS SP password CRLF 198187770Sluigi = { 199187770Sluigi pass((char *) $3); 200187770Sluigi free((char *) $3); 201187770Sluigi } 202187770Sluigi | PORT check_login SP host_port CRLF 203187770Sluigi = { 204187770Sluigi usedefault = 0; 205187770Sluigi if (pdata >= 0) { 206187770Sluigi (void) close(pdata); 207187770Sluigi pdata = -1; 208220802Sglebius } 209187770Sluigi/* H* port fix, part B: admonish the twit. 210187770Sluigi Also require login before PORT works */ 211220802Sglebius if ($2) { 212187770Sluigi if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) { 213187770Sluigi reply(200, "PORT command successful."); 214220802Sglebius } else { 215220802Sglebius syslog (LOG_WARNING, "refused %s from %s", 216220804Sglebius cbuf, remotehost); 217187770Sluigi reply(500, "You've GOT to be joking."); 218187770Sluigi } 219187770Sluigi } 220220802Sglebius } 221187770Sluigi/* | PASV CRLF 222187770Sluigi = { 223187770Sluigi passive(); 224220802Sglebius } */ 225187770Sluigi | PASV check_login CRLF 226187770Sluigi = { 227187770Sluigi/* Require login for PASV, too. This actually fixes a bug -- telnet to an 228220802Sglebius unfixed wu-ftpd and type PASV first off, and it crashes! */ 229187770Sluigi if ($2) { 230220802Sglebius passive(); 231220802Sglebius } 232187770Sluigi } 233187770Sluigi | TYPE SP type_code CRLF 234187770Sluigi = { 235187770Sluigi switch (cmd_type) { 236187770Sluigi 237187770Sluigi case TYPE_A: 238187770Sluigi if (cmd_form == FORM_N) { 239187770Sluigi reply(200, "Type set to A."); 240187770Sluigi type = cmd_type; 241187770Sluigi form = cmd_form; 242187770Sluigi } else 243187770Sluigi reply(504, "Form must be N."); 244220802Sglebius break; 245187770Sluigi 246187770Sluigi case TYPE_E: 247220802Sglebius reply(504, "Type E not implemented."); 248187770Sluigi break; 249187770Sluigi 250187770Sluigi case TYPE_I: 251187770Sluigi reply(200, "Type set to I."); 252220802Sglebius type = cmd_type; 253187770Sluigi break; 254187770Sluigi 255187770Sluigi case TYPE_L: 256187770Sluigi#if NBBY == 8 257187770Sluigi if (cmd_bytesz == 8) { 258187770Sluigi reply(200, 259187770Sluigi "Type set to L (byte size 8)."); 260187770Sluigi type = cmd_type; 261188294Spiso } else 262188294Spiso reply(504, "Byte size must be 8."); 263188294Spiso#else /* NBBY == 8 */ 264187770Sluigi UNIMPLEMENTED for NBBY != 8 265187770Sluigi#endif /* NBBY == 8 */ 266220802Sglebius } 267220802Sglebius } 268220802Sglebius | STRU SP struct_code CRLF 269187770Sluigi = { 270187770Sluigi switch ($3) { 271187770Sluigi 272187770Sluigi case STRU_F: 273187770Sluigi reply(200, "STRU F ok."); 274187770Sluigi break; 275187770Sluigi 276187770Sluigi default: 277187770Sluigi reply(504, "Unimplemented STRU type."); 278187770Sluigi } 279187770Sluigi } 280187770Sluigi | MODE SP mode_code CRLF 281187770Sluigi = { 282187770Sluigi switch ($3) { 283187770Sluigi 284187770Sluigi case MODE_S: 285220802Sglebius reply(200, "MODE S ok."); 286220802Sglebius break; 287220802Sglebius 288187770Sluigi default: 289187770Sluigi reply(502, "Unimplemented MODE type."); 290220802Sglebius } 291220802Sglebius } 292187770Sluigi | ALLO SP NUMBER CRLF 293220802Sglebius = { 294187770Sluigi reply(202, "ALLO command ignored."); 295187770Sluigi } 296220802Sglebius | ALLO SP NUMBER SP R SP NUMBER CRLF 297187770Sluigi = { 298187770Sluigi reply(202, "ALLO command ignored."); 299187770Sluigi } 300187770Sluigi | RETR check_login SP pathname CRLF 301220802Sglebius = { 302187770Sluigi if ($2 && $4) 303187770Sluigi retrieve((char *) 0, (char *) $4); 304187770Sluigi if ($4) 305187770Sluigi free((char *) $4); 306187770Sluigi } 307220802Sglebius | STOR check_login SP pathname CRLF 308187770Sluigi = { 309220802Sglebius if ($2 && $4) 310220802Sglebius store((char *) $4, "w", 0); 311187770Sluigi if ($4) 312187770Sluigi free((char *) $4); 313187770Sluigi } 314187770Sluigi | APPE check_login SP pathname CRLF 315220835Sglebius = { 316187770Sluigi if ($2 && $4) 317220835Sglebius store((char *) $4, "a", 0); 318223185Sglebius if ($4) 319223185Sglebius free((char *) $4); 320220835Sglebius } 321223185Sglebius | NLST check_login CRLF 322223185Sglebius = { 323223185Sglebius if ($2) 324220835Sglebius send_file_list("."); 325220835Sglebius } 326223185Sglebius | NLST check_login SP STRING CRLF 327223185Sglebius = { 328223185Sglebius if ($2 && $4) 329223185Sglebius send_file_list((char *) $4); 330223185Sglebius if ($4) 331220835Sglebius free((char *) $4); 332220835Sglebius } 333220835Sglebius | LIST check_login CRLF 334220835Sglebius = { 335220835Sglebius if ($2) 336220835Sglebius retrieve(LS_COMMAND, ""); 337220804Sglebius } 338220835Sglebius | LIST check_login SP pathname CRLF 339220835Sglebius = { 340187770Sluigi if ($2 && $4) 341220835Sglebius { 342187770Sluigi char buffer[sizeof(LS_COMMAND)+3]; 343220835Sglebius strcpy(buffer, LS_COMMAND); 344220835Sglebius strcat(buffer, " %s"); 345220835Sglebius retrieve(buffer, (char *) $4); 346220835Sglebius } 347187770Sluigi if ($4) 348228051Sglebius free((char *) $4); 349220835Sglebius } 350220835Sglebius | STAT check_login SP pathname CRLF 351220835Sglebius = { 352187770Sluigi if ($2 && $4) 353228051Sglebius statfilecmd((char *) $4); 354187770Sluigi if ($4) 355220835Sglebius free((char *) $4); 356220835Sglebius } 357220835Sglebius | STAT CRLF 358220835Sglebius = { 359187770Sluigi statcmd(); 360187770Sluigi } 361220835Sglebius | DELE check_login SP pathname CRLF 362187770Sluigi = { 363187770Sluigi if ($2 && $4) 364220835Sglebius delete((char *) $4); 365220835Sglebius if ($4) 366220835Sglebius free((char *) $4); 367220835Sglebius } 368220835Sglebius | RNTO SP pathname CRLF 369220835Sglebius = { 370220835Sglebius if (fromname) { 371220835Sglebius renamecmd(fromname, (char *) $3); 372220835Sglebius free(fromname); 373220835Sglebius fromname = (char *) 0; 374220835Sglebius } else { 375220835Sglebius reply(503, "Bad sequence of commands."); 376220835Sglebius } 377220835Sglebius free((char *) $3); 378220835Sglebius } 379223185Sglebius | ABOR CRLF 380223185Sglebius = { 381220835Sglebius reply(225, "ABOR command successful."); 382223185Sglebius } 383223185Sglebius | CWD check_login CRLF 384223185Sglebius = { 385187770Sluigi if ($2) 386220835Sglebius cwd(pw->pw_dir); 387223185Sglebius } 388223185Sglebius | CWD check_login SP pathname CRLF 389223185Sglebius = { 390223185Sglebius if ($2 && $4) 391223185Sglebius cwd((char *) $4); 392220835Sglebius if ($4) 393187770Sluigi free((char *) $4); 394187770Sluigi } 395187770Sluigi | HELP CRLF 396220835Sglebius = { 397187770Sluigi help(cmdtab, (char *) 0); 398187770Sluigi } 399220835Sglebius | HELP SP STRING CRLF 400220835Sglebius = { 401187770Sluigi register char *cp = (char *)$3; 402220804Sglebius 403187770Sluigi if (strncasecmp(cp, "SITE", 4) == 0) { 404220804Sglebius cp = (char *)$3 + 4; 405187770Sluigi if (*cp == ' ') 406220835Sglebius cp++; 407187770Sluigi if (*cp) 408220835Sglebius help(sitetab, cp); 409220835Sglebius else 410220835Sglebius help(sitetab, (char *) 0); 411220835Sglebius } else 412187770Sluigi help(cmdtab, (char *) $3); 413187770Sluigi } 414187770Sluigi | NOOP CRLF 415220835Sglebius = { 416220835Sglebius reply(200, "NOOP command successful."); 417220835Sglebius } 418187770Sluigi | MKD check_login SP pathname CRLF 419187770Sluigi = { 420187770Sluigi if ($2 && $4) 421187770Sluigi makedir((char *) $4); 422220835Sglebius if ($4) 423187770Sluigi free((char *) $4); 424187770Sluigi } 425187770Sluigi | RMD check_login SP pathname CRLF 426220835Sglebius = { 427187770Sluigi if ($2 && $4) 428188294Spiso removedir((char *) $4); 429220802Sglebius if ($4) 430220802Sglebius free((char *) $4); 431188294Spiso } 432188294Spiso | PWD check_login CRLF 433188294Spiso = { 434220835Sglebius if ($2) 435188294Spiso pwd(); 436220835Sglebius } 437220835Sglebius | CDUP check_login CRLF 438188294Spiso = { 439220835Sglebius if ($2) 440188294Spiso cwd(".."); 441220835Sglebius } 442220835Sglebius | SITE SP HELP CRLF 443220835Sglebius = { 444188294Spiso help(sitetab, (char *) 0); 445187770Sluigi } 446188294Spiso | SITE SP HELP SP STRING CRLF 447188294Spiso = { 448188294Spiso help(sitetab, (char *) $5); 449187770Sluigi } 450220835Sglebius | SITE SP UMASK check_login CRLF 451187770Sluigi = { 452187770Sluigi int oldmask; 453187770Sluigi 454187770Sluigi if ($4) { 455220835Sglebius oldmask = umask(0); 456220835Sglebius (void) umask(oldmask); 457187770Sluigi reply(200, "Current UMASK is %03o", oldmask); 458220835Sglebius } 459187770Sluigi } 460187770Sluigi | SITE SP UMASK check_login SP octal_number CRLF 461187770Sluigi = { 462220835Sglebius int oldmask; 463220835Sglebius 464187770Sluigi if ($4) { 465187770Sluigi if (($6 == -1) || ($6 > 0777)) { 466187770Sluigi reply(501, "Bad UMASK value"); 467187770Sluigi } else { 468188294Spiso oldmask = umask($6); 469188294Spiso reply(200, 470188294Spiso "UMASK set to %03o (was %03o)", 471188294Spiso $6, oldmask); 472187770Sluigi } 473220835Sglebius } 474187770Sluigi } 475187770Sluigi | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 476187770Sluigi = { 477220804Sglebius if ($4 && $8) { 478220802Sglebius if ($6 > 0777) 479223185Sglebius reply(501, 480187770Sluigi "CHMOD: Mode value must be between 0 and 0777"); 481187770Sluigi else if (chmod((char *) $8, $6) < 0) 482223185Sglebius perror_reply(550, (char *) $8); 483220835Sglebius else 484220835Sglebius reply(200, "CHMOD command successful."); 485187770Sluigi } 486220835Sglebius if ($8) 487187770Sluigi free((char *) $8); 488187770Sluigi } 489220802Sglebius | SITE SP IDLE CRLF 490187770Sluigi = { 491220835Sglebius reply(200, 492187770Sluigi "Current IDLE time limit is %d seconds; max %d", 493220835Sglebius timeout, maxtimeout); 494187770Sluigi } 495187770Sluigi | SITE SP IDLE SP NUMBER CRLF 496187770Sluigi = { 497187770Sluigi if ($5 < 30 || $5 > maxtimeout) { 498187770Sluigi reply(501, 499187770Sluigi "Maximum IDLE time must be between 30 and %d seconds", 500187770Sluigi maxtimeout); 501187770Sluigi } else { 502220802Sglebius timeout = $5; 503187770Sluigi (void) alarm((unsigned) timeout); 504187770Sluigi reply(200, 505187770Sluigi "Maximum IDLE time set to %d seconds", 506220835Sglebius timeout); 507187770Sluigi } 508187770Sluigi } 509187770Sluigi | STOU check_login SP pathname CRLF 510220802Sglebius = { 511187770Sluigi if ($2 && $4) 512220802Sglebius store((char *) $4, "w", 1); 513187770Sluigi if ($4) 514187770Sluigi free((char *) $4); 515220835Sglebius } 516220835Sglebius | SYST CRLF 517220835Sglebius = { 518220835Sglebius#ifdef unix 519220835Sglebius#ifdef BSD 520187770Sluigi reply(215, "UNIX Type: L%d Version: BSD-%d", 521220835Sglebius NBBY, BSD); 522220835Sglebius#else /* BSD */ 523188294Spiso reply(215, "UNIX Type: L%d", NBBY); 524220835Sglebius#endif /* BSD */ 525220835Sglebius#else /* unix */ 526220835Sglebius reply(215, "UNKNOWN Type: L%d", NBBY); 527188294Spiso#endif /* unix */ 528188294Spiso } 529188294Spiso 530188294Spiso /* 531188294Spiso * SIZE is not in RFC959, but Postel has blessed it and 532188294Spiso * it will be in the updated RFC. 533188294Spiso * 534188294Spiso * Return size of file in a format suitable for 535220835Sglebius * using with RESTART (we just count bytes). 536220835Sglebius */ 537188294Spiso | SIZE check_login SP pathname CRLF 538188294Spiso = { 539220835Sglebius if ($2 && $4) 540188294Spiso sizecmd((char *) $4); 541188294Spiso if ($4) 542188294Spiso free((char *) $4); 543188294Spiso } 544188294Spiso 545188294Spiso /* 546188294Spiso * MDTM is not in RFC959, but Postel has blessed it and 547220835Sglebius * it will be in the updated RFC. 548188294Spiso * 549220804Sglebius * Return modification time of file as an ISO 3307 550187770Sluigi * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 551220835Sglebius * where xxx is the fractional second (of any precision, 552187770Sluigi * not necessarily 3 digits) 553187770Sluigi */ 554187770Sluigi | MDTM check_login SP pathname CRLF 555220835Sglebius = { 556187770Sluigi if ($2 && $4) { 557187770Sluigi struct stat stbuf; 558187770Sluigi if (stat((char *) $4, &stbuf) < 0) 559187770Sluigi perror_reply(550, (char *) $4); 560220835Sglebius else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 561187770Sluigi reply(550, "%s: not a plain file.", 562220835Sglebius (char *) $4); 563187770Sluigi } else { 564220835Sglebius register struct tm *t; 565220804Sglebius struct tm *gmtime(); 566220835Sglebius t = gmtime(&stbuf.st_mtime); 567187770Sluigi reply(213, 568220835Sglebius "19%02d%02d%02d%02d%02d%02d", 569220835Sglebius t->tm_year, t->tm_mon+1, t->tm_mday, 570220835Sglebius t->tm_hour, t->tm_min, t->tm_sec); 571220835Sglebius } 572187770Sluigi } 573187770Sluigi if ($4) 574220804Sglebius free((char *) $4); 575220835Sglebius } 576187770Sluigi | QUIT CRLF 577220835Sglebius = { 578187770Sluigi reply(221, "Goodbye."); 579187770Sluigi dologout(0); 580187770Sluigi } 581220835Sglebius | error CRLF 582220804Sglebius = { 583187770Sluigi yyerrok; 584187770Sluigi } 585187770Sluigi ; 586220835Sglebiusrcmd: RNFR check_login SP pathname CRLF 587187770Sluigi = { 588220835Sglebius char *renamefrom(); 589220804Sglebius 590187770Sluigi restart_point = (off_t) 0; 591187770Sluigi if ($2 && $4) { 592187770Sluigi fromname = renamefrom((char *) $4); 593220835Sglebius if (fromname == (char *) 0 && $4) { 594220804Sglebius free((char *) $4); 595220804Sglebius } 596187770Sluigi } 597187770Sluigi } 598223185Sglebius | REST SP byte_size CRLF 599220835Sglebius = { 600220835Sglebius long atol(); 601220804Sglebius 602187770Sluigi fromname = (char *) 0; 603187770Sluigi restart_point = $3; 604220804Sglebius reply(350, "Restarting at %ld. %s", restart_point, 605187770Sluigi "Send STORE or RETRIEVE to initiate transfer."); 606223185Sglebius } 607220835Sglebius ; 608220835Sglebius 609187770Sluigiusername: STRING 610220804Sglebius ; 611187770Sluigi 612220835Sglebiuspassword: /* empty */ 613187770Sluigi = { 614187770Sluigi *(char **)&($$) = (char *)calloc(1, sizeof(char)); 615187770Sluigi } 616187770Sluigi | STRING 617187770Sluigi ; 618187770Sluigi 619187770Sluigibyte_size: NUMBER 620187770Sluigi ; 621187770Sluigi 622187770Sluigihost_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 623187770Sluigi NUMBER COMMA NUMBER 624187770Sluigi = { 625187770Sluigi register char *a, *p; 626187770Sluigi 627187770Sluigi a = (char *)&data_dest.sin_addr; 628187770Sluigi a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 629187770Sluigi 630187770Sluigi/* H* port fix, part A-1: Check the args against the client addr */ 631187770Sluigi p = (char *)&his_addr.sin_addr; 632187770Sluigi if (memcmp (a, p, sizeof (data_dest.sin_addr))) 633187770Sluigi memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */ 634187770Sluigi 635187770Sluigi p = (char *)&data_dest.sin_port; 636187770Sluigi 637187770Sluigi/* H* port fix, part A-2: only allow client ports in "user space" */ 638187770Sluigi p[0] = 0; p[1] = 0; 639187770Sluigi cliport = ($9 << 8) + $11; 640187770Sluigi if (cliport > 1023) { 641187770Sluigi p[0] = $9; p[1] = $11; 642187770Sluigi } 643223080Sae 644223080Sae p[0] = $9; p[1] = $11; 645223080Sae data_dest.sin_family = AF_INET; 646187770Sluigi } 647187770Sluigi ; 648187770Sluigi 649187770Sluigiform_code: N 650187770Sluigi = { 651187770Sluigi $$ = FORM_N; 652187770Sluigi } 653187770Sluigi | T 654187770Sluigi = { 655187770Sluigi $$ = FORM_T; 656187770Sluigi } 657187770Sluigi | C 658187770Sluigi = { 659187770Sluigi $$ = FORM_C; 660187770Sluigi } 661187770Sluigi ; 662187770Sluigi 663187770Sluigitype_code: A 664187770Sluigi = { 665187770Sluigi cmd_type = TYPE_A; 666187770Sluigi cmd_form = FORM_N; 667187770Sluigi } 668187770Sluigi | A SP form_code 669187770Sluigi = { 670187770Sluigi cmd_type = TYPE_A; 671187770Sluigi cmd_form = $3; 672187770Sluigi } 673187770Sluigi | E 674220802Sglebius = { 675187770Sluigi cmd_type = TYPE_E; 676187770Sluigi cmd_form = FORM_N; 677187770Sluigi } 678187770Sluigi | E SP form_code 679187770Sluigi = { 680187770Sluigi cmd_type = TYPE_E; 681187770Sluigi cmd_form = $3; 682187770Sluigi } 683187770Sluigi | I 684187770Sluigi = { 685187770Sluigi cmd_type = TYPE_I; 686187770Sluigi } 687220802Sglebius | L 688187770Sluigi = { 689187770Sluigi cmd_type = TYPE_L; 690187770Sluigi cmd_bytesz = NBBY; 691187770Sluigi } 692187770Sluigi | L SP byte_size 693187770Sluigi = { 694220802Sglebius cmd_type = TYPE_L; 695187770Sluigi cmd_bytesz = $3; 696187770Sluigi } 697187770Sluigi /* this is for a bug in the BBN ftp */ 698187770Sluigi | L byte_size 699187770Sluigi = { 700187770Sluigi cmd_type = TYPE_L; 701220802Sglebius cmd_bytesz = $2; 702187770Sluigi } 703187770Sluigi ; 704187770Sluigi 705187770Sluigistruct_code: F 706187770Sluigi = { 707187770Sluigi $$ = STRU_F; 708187770Sluigi } 709187770Sluigi | R 710187770Sluigi = { 711220802Sglebius $$ = STRU_R; 712187770Sluigi } 713187770Sluigi | P 714187770Sluigi = { 715187770Sluigi $$ = STRU_P; 716187770Sluigi } 717187770Sluigi ; 718220802Sglebius 719187770Sluigimode_code: S 720187770Sluigi = { 721187770Sluigi $$ = MODE_S; 722187770Sluigi } 723187770Sluigi | B 724187770Sluigi = { 725187770Sluigi $$ = MODE_B; 726187770Sluigi } 727187770Sluigi | C 728187770Sluigi = { 729187770Sluigi $$ = MODE_C; 730187770Sluigi } 731187770Sluigi ; 732187770Sluigi 733187770Sluigipathname: pathstring 734187770Sluigi = { 735187770Sluigi /* 736187770Sluigi * Problem: this production is used for all pathname 737220802Sglebius * processing, but only gives a 550 error reply. 738220835Sglebius * This is a valid reply in some cases but not in others. 739223079Sae */ 740220835Sglebius if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 741220804Sglebius *(char **)&($$) = *ftpglob((char *) $1); 742223499Sglebius if (globerr != NULL) { 743223499Sglebius reply(550, globerr); 744187770Sluigi/* $$ = NULL; */ 745223079Sae $$ = 0; 746187770Sluigi } 747223079Sae free((char *) $1); 748223079Sae } else 749223079Sae $$ = $1; 750223079Sae } 751223499Sglebius ; 752223499Sglebius 753220802Sglebiuspathstring: STRING 754187770Sluigi ; 755187770Sluigi 756220835Sglebiusoctal_number: NUMBER 757220835Sglebius = { 758220835Sglebius register int ret, dec, multby, digit; 759220835Sglebius 760220835Sglebius /* 761223499Sglebius * Convert a number that was read as decimal number 762223499Sglebius * to what it would be if it had been read as octal. 763220835Sglebius */ 764220835Sglebius dec = $1; 765220835Sglebius multby = 1; 766223499Sglebius ret = 0; 767223499Sglebius while (dec) { 768223080Sae digit = dec%10; 769220835Sglebius if (digit > 7) { 770220835Sglebius ret = -1; 771220835Sglebius break; 772223080Sae } 773220835Sglebius ret += digit * multby; 774220835Sglebius multby *= 8; 775220835Sglebius dec /= 10; 776220835Sglebius } 777220835Sglebius $$ = ret; 778220835Sglebius } 779220835Sglebius ; 780220835Sglebius 781220835Sglebiuscheck_login: /* empty */ 782220835Sglebius = { 783223499Sglebius if (logged_in) 784223499Sglebius $$ = 1; 785220835Sglebius else { 786220835Sglebius reply(530, "Please login with USER and PASS."); 787220835Sglebius $$ = 0; 788220835Sglebius } 789220835Sglebius } 790223499Sglebius ; 791223499Sglebius 792220835Sglebius%% 793223499Sglebius 794223499Sglebiusextern jmp_buf errcatch; 795223185Sglebius 796223416Sglebius#define CMD 0 /* beginning of command */ 797223499Sglebius#define ARGS 1 /* expect miscellaneous arguments */ 798223499Sglebius#define STR1 2 /* expect SP followed by STRING */ 799223416Sglebius#define STR2 3 /* expect STRING */ 800220835Sglebius#define OSTR 4 /* optional SP then STRING */ 801220835Sglebius#define ZSTR1 5 /* SP then optional STRING */ 802220835Sglebius#define ZSTR2 6 /* optional STRING after SP */ 803220835Sglebius#define SITECMD 7 /* SITE command */ 804220835Sglebius#define NSTR 8 /* Number followed by a string */ 805220835Sglebius 806223499Sglebiusstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 807223499Sglebius { "USER", USER, STR1, 1, "<sp> username" }, 808223185Sglebius { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 809223416Sglebius { "ACCT", ACCT, STR1, 0, "(specify account)" }, 810223499Sglebius { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 811223499Sglebius { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 812223416Sglebius { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 813223416Sglebius { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 814223499Sglebius { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 815223499Sglebius { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 816223416Sglebius { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 817220835Sglebius { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 818220835Sglebius { "RETR", RETR, STR1, 1, "<sp> file-name" }, 819220835Sglebius { "STOR", STOR, STR1, 1, "<sp> file-name" }, 820220835Sglebius { "APPE", APPE, STR1, 1, "<sp> file-name" }, 821220835Sglebius { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 822220835Sglebius { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 823220835Sglebius { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 824220835Sglebius { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 825220835Sglebius { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 826220835Sglebius { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 827220835Sglebius { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 828220835Sglebius { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 829220835Sglebius { "REST", REST, ARGS, 1, "(restart command)" }, 830220835Sglebius { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 831220835Sglebius { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 832187770Sluigi { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 833187770Sluigi { "DELE", DELE, STR1, 1, "<sp> file-name" }, 834223499Sglebius { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 835223499Sglebius { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 836187770Sluigi { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 837187770Sluigi { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 838220802Sglebius { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 839187770Sluigi { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 840187770Sluigi { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 841220802Sglebius { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 842187770Sluigi { "NOOP", NOOP, ARGS, 1, "" }, 843223499Sglebius { "MKD", MKD, STR1, 1, "<sp> path-name" }, 844223499Sglebius { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 845220802Sglebius { "RMD", RMD, STR1, 1, "<sp> path-name" }, 846187770Sluigi { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 847220802Sglebius { "PWD", PWD, ARGS, 1, "(return current directory)" }, 848187770Sluigi { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 849187770Sluigi { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 850223499Sglebius { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 851223499Sglebius { "STOU", STOU, STR1, 1, "<sp> file-name" }, 852187770Sluigi { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 853187770Sluigi { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 854187770Sluigi { NULL, 0, 0, 0, 0 } 855187770Sluigi}; 856187770Sluigi 857187770Sluigistruct tab sitetab[] = { 858187770Sluigi { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 859187770Sluigi { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 860187770Sluigi { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 861187770Sluigi { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 862187770Sluigi { NULL, 0, 0, 0, 0 } 863187770Sluigi}; 864187770Sluigi 865223080Saestruct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd) 866223080Sae{ 867223080Sae 868187770Sluigi for (; p->name != NULL; p++) 869187770Sluigi if (strcmp(cmd, p->name) == 0) 870187770Sluigi return (p); 871187770Sluigi return (0); 872187770Sluigi} 873187770Sluigi 874187770Sluigi#include <arpa/telnet.h> 875187770Sluigi 876187770Sluigi/* 877220802Sglebius * getline - a hacked up version of fgets to ignore TELNET escape codes. 878220802Sglebius */ 879220802Sglebiuschar *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop) 880187770Sluigi{ 881187770Sluigi register c; 882187770Sluigi register char *cs; 883187770Sluigi 884187770Sluigi cs = s; 885187770Sluigi/* tmpline may contain saved command from urgent mode interruption */ 886220835Sglebius for (c = 0; *(tmpline + c) && --n > 0; ++c) { 887220802Sglebius *cs++ = *(tmpline + c); 888187770Sluigi if (*(tmpline + c) == '\n') { 889220835Sglebius *cs++ = '\0'; 890220802Sglebius if (debug) 891187770Sluigi syslog(LOG_DEBUG, "command: %s", s); 892220835Sglebius *tmpline = '\0'; 893187770Sluigi return(s); 894187770Sluigi } 895187770Sluigi if (c == 0) 896187770Sluigi *tmpline = '\0'; 897187770Sluigi } 898187770Sluigi while ((c = getc(iop)) != EOF) { 899187770Sluigi c &= 0377; 900187770Sluigi if (c == IAC) { 901187770Sluigi if ((c = getc(iop)) != EOF) { 902187770Sluigi c &= 0377; 903187770Sluigi switch (c) { 904187770Sluigi case WILL: 905187770Sluigi case WONT: 906187770Sluigi c = getc(iop); 907187770Sluigi printf("%c%c%c", IAC, DONT, 0377&c); 908189395Sluigi (void) fflush(stdout); 909189395Sluigi continue; 910187770Sluigi case DO: 911187770Sluigi case DONT: 912187770Sluigi c = getc(iop); 913187770Sluigi printf("%c%c%c", IAC, WONT, 0377&c); 914187770Sluigi (void) fflush(stdout); 915187770Sluigi continue; 916187770Sluigi case IAC: 917187770Sluigi break; 918187770Sluigi default: 919187770Sluigi continue; /* ignore command */ 920187770Sluigi } 921187770Sluigi } 922187770Sluigi } 923187770Sluigi *cs++ = c; 924187770Sluigi if (--n <= 0 || c == '\n') 925187770Sluigi break; 926187770Sluigi } 927187770Sluigi if (c == EOF && cs == s) 928187770Sluigi return (NULL); 929187770Sluigi *cs++ = '\0'; 930223499Sglebius if (debug) 931223499Sglebius syslog(LOG_DEBUG, "command: %s", s); 932187770Sluigi return (s); 933187770Sluigi} 934187770Sluigi 935187770Sluigistatic VOIDRET toolong FUNCTION((input), int input) 936187770Sluigi{ 937187770Sluigi time_t now; 938187770Sluigi 939220802Sglebius reply(421, "Timeout (%d seconds): closing control connection.", timeout); 940187770Sluigi (void) time(&now); 941187770Sluigi syslog(LOG_INFO, "User %s timed out after %d seconds at %s", 942187770Sluigi (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 943187770Sluigi dologout(1); 944187770Sluigi} 945187770Sluigi 946220804Sglebiusint yylex FUNCTION_NOARGS 947187770Sluigi{ 948187770Sluigi static int cpos, state; 949187770Sluigi register char *cp, *cp2; 950187770Sluigi register struct tab *p; 951187770Sluigi int n; 952187770Sluigi char c, *copy(); 953187770Sluigi 954187770Sluigi for (;;) { 955187770Sluigi switch (state) { 956187770Sluigi 957187770Sluigi case CMD: 958187770Sluigi (void) signal(SIGALRM, toolong); 959187770Sluigi (void) alarm((unsigned) timeout); 960187770Sluigi if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 961187770Sluigi reply(221, "You could at least say goodbye."); 962187770Sluigi dologout(0); 963187770Sluigi } 964187770Sluigi (void) alarm(0); 965187770Sluigi#ifdef SETPROCTITLE 966187770Sluigi if (strncasecmp(cbuf, "PASS", 4) != NULL) 967187770Sluigi setproctitle("%s: %s", proctitle, cbuf); 968187770Sluigi#endif /* SETPROCTITLE */ 969187770Sluigi if ((cp = strchr(cbuf, '\r'))) { 970187770Sluigi *cp++ = '\n'; 971220802Sglebius *cp = '\0'; 972187770Sluigi } 973187770Sluigi if ((cp = strpbrk(cbuf, " \n"))) 974187770Sluigi cpos = cp - cbuf; 975187770Sluigi if (cpos == 0) 976187770Sluigi cpos = 4; 977187770Sluigi c = cbuf[cpos]; 978187770Sluigi cbuf[cpos] = '\0'; 979187770Sluigi upper(cbuf); 980187770Sluigi p = lookup(cmdtab, cbuf); 981187770Sluigi cbuf[cpos] = c; 982187770Sluigi if (p != 0) { 983187770Sluigi if (p->implemented == 0) { 984187770Sluigi nack(p->name); 985187770Sluigi longjmp(errcatch,0); 986187770Sluigi /* NOTREACHED */ 987187770Sluigi } 988187770Sluigi state = p->state; 989 *(char **)&yylval = p->name; 990 return (p->token); 991 } 992 break; 993 994 case SITECMD: 995 if (cbuf[cpos] == ' ') { 996 cpos++; 997 return (SP); 998 } 999 cp = &cbuf[cpos]; 1000 if ((cp2 = strpbrk(cp, " \n"))) 1001 cpos = cp2 - cbuf; 1002 c = cbuf[cpos]; 1003 cbuf[cpos] = '\0'; 1004 upper(cp); 1005 p = lookup(sitetab, cp); 1006 cbuf[cpos] = c; 1007 if (p != 0) { 1008 if (p->implemented == 0) { 1009 state = CMD; 1010 nack(p->name); 1011 longjmp(errcatch,0); 1012 /* NOTREACHED */ 1013 } 1014 state = p->state; 1015 *(char **)&yylval = p->name; 1016 return (p->token); 1017 } 1018 state = CMD; 1019 break; 1020 1021 case OSTR: 1022 if (cbuf[cpos] == '\n') { 1023 state = CMD; 1024 return (CRLF); 1025 } 1026 /* FALLTHROUGH */ 1027 1028 case STR1: 1029 case ZSTR1: 1030 dostr1: 1031 if (cbuf[cpos] == ' ') { 1032 cpos++; 1033 state = state == OSTR ? STR2 : ++state; 1034 return (SP); 1035 } 1036 break; 1037 1038 case ZSTR2: 1039 if (cbuf[cpos] == '\n') { 1040 state = CMD; 1041 return (CRLF); 1042 } 1043 /* FALLTHROUGH */ 1044 1045 case STR2: 1046 cp = &cbuf[cpos]; 1047 n = strlen(cp); 1048 cpos += n - 1; 1049 /* 1050 * Make sure the string is nonempty and \n terminated. 1051 */ 1052 if (n > 1 && cbuf[cpos] == '\n') { 1053 cbuf[cpos] = '\0'; 1054 *(char **)&yylval = copy(cp); 1055 cbuf[cpos] = '\n'; 1056 state = ARGS; 1057 return (STRING); 1058 } 1059 break; 1060 1061 case NSTR: 1062 if (cbuf[cpos] == ' ') { 1063 cpos++; 1064 return (SP); 1065 } 1066 if (isdigit(cbuf[cpos])) { 1067 cp = &cbuf[cpos]; 1068 while (isdigit(cbuf[++cpos])) 1069 ; 1070 c = cbuf[cpos]; 1071 cbuf[cpos] = '\0'; 1072 yylval = atoi(cp); 1073 cbuf[cpos] = c; 1074 state = STR1; 1075 return (NUMBER); 1076 } 1077 state = STR1; 1078 goto dostr1; 1079 1080 case ARGS: 1081 if (isdigit(cbuf[cpos])) { 1082 cp = &cbuf[cpos]; 1083 while (isdigit(cbuf[++cpos])) 1084 ; 1085 c = cbuf[cpos]; 1086 cbuf[cpos] = '\0'; 1087 yylval = atoi(cp); 1088 cbuf[cpos] = c; 1089 return (NUMBER); 1090 } 1091 switch (cbuf[cpos++]) { 1092 1093 case '\n': 1094 state = CMD; 1095 return (CRLF); 1096 1097 case ' ': 1098 return (SP); 1099 1100 case ',': 1101 return (COMMA); 1102 1103 case 'A': 1104 case 'a': 1105 return (A); 1106 1107 case 'B': 1108 case 'b': 1109 return (B); 1110 1111 case 'C': 1112 case 'c': 1113 return (C); 1114 1115 case 'E': 1116 case 'e': 1117 return (E); 1118 1119 case 'F': 1120 case 'f': 1121 return (F); 1122 1123 case 'I': 1124 case 'i': 1125 return (I); 1126 1127 case 'L': 1128 case 'l': 1129 return (L); 1130 1131 case 'N': 1132 case 'n': 1133 return (N); 1134 1135 case 'P': 1136 case 'p': 1137 return (P); 1138 1139 case 'R': 1140 case 'r': 1141 return (R); 1142 1143 case 'S': 1144 case 's': 1145 return (S); 1146 1147 case 'T': 1148 case 't': 1149 return (T); 1150 1151 } 1152 break; 1153 1154 default: 1155 opiefatal("Unknown state in scanner."); 1156 } 1157 yyerror((char *) 0); 1158 state = CMD; 1159 longjmp(errcatch,0); 1160 } 1161} 1162 1163VOIDRET upper FUNCTION((s), char *s) 1164{ 1165 while (*s != '\0') { 1166 if (islower(*s)) 1167 *s = toupper(*s); 1168 s++; 1169 } 1170} 1171 1172char *copy FUNCTION((s), char *s) 1173{ 1174 char *p; 1175 1176 p = malloc((unsigned) strlen(s) + 1); 1177 if (p == NULL) 1178 opiefatal("Ran out of memory."); 1179 (void) strcpy(p, s); 1180 return (p); 1181} 1182 1183VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s) 1184{ 1185 register struct tab *c; 1186 register int width, NCMDS; 1187 char *type; 1188 1189 if (ctab == sitetab) 1190 type = "SITE "; 1191 else 1192 type = ""; 1193 width = 0, NCMDS = 0; 1194 for (c = ctab; c->name != NULL; c++) { 1195 int len = strlen(c->name); 1196 1197 if (len > width) 1198 width = len; 1199 NCMDS++; 1200 } 1201 width = (width + 8) &~ 7; 1202 if (s == 0) { 1203 register int i, j, w; 1204 int columns, lines; 1205 1206 lreply(214, "The following %scommands are recognized %s.", 1207 type, "(* =>'s unimplemented)"); 1208 columns = 76 / width; 1209 if (columns == 0) 1210 columns = 1; 1211 lines = (NCMDS + columns - 1) / columns; 1212 for (i = 0; i < lines; i++) { 1213 printf(" "); 1214 for (j = 0; j < columns; j++) { 1215 c = ctab + j * lines + i; 1216 printf("%s%c", c->name, 1217 c->implemented ? ' ' : '*'); 1218 if (c + lines >= &ctab[NCMDS]) 1219 break; 1220 w = strlen(c->name) + 1; 1221 while (w < width) { 1222 putchar(' '); 1223 w++; 1224 } 1225 } 1226 printf("\r\n"); 1227 } 1228 (void) fflush(stdout); 1229 return; 1230 } 1231 upper(s); 1232 c = lookup(ctab, s); 1233 if (c == (struct tab *)0) { 1234 reply(502, "Unknown command %s.", s); 1235 return; 1236 } 1237 if (c->implemented) 1238 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1239 else 1240 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1241 c->name, c->help); 1242} 1243 1244VOIDRET sizecmd FUNCTION((filename), char *filename) 1245{ 1246 switch (type) { 1247 case TYPE_L: 1248 case TYPE_I: { 1249 struct stat stbuf; 1250 if (stat(filename, &stbuf) < 0 || 1251 (stbuf.st_mode&S_IFMT) != S_IFREG) 1252 reply(550, "%s: not a plain file.", filename); 1253 else 1254 reply(213, "%lu", stbuf.st_size); 1255 break;} 1256 case TYPE_A: { 1257 FILE *fin; 1258 register int c; 1259 register long count; 1260 struct stat stbuf; 1261 fin = fopen(filename, "r"); 1262 if (fin == NULL) { 1263 perror_reply(550, filename); 1264 return; 1265 } 1266 if (fstat(fileno(fin), &stbuf) < 0 || 1267 (stbuf.st_mode&S_IFMT) != S_IFREG) { 1268 reply(550, "%s: not a plain file.", filename); 1269 (void) fclose(fin); 1270 return; 1271 } 1272 1273 count = 0; 1274 while((c=getc(fin)) != EOF) { 1275 if (c == '\n') /* will get expanded to \r\n */ 1276 count++; 1277 count++; 1278 } 1279 (void) fclose(fin); 1280 1281 reply(213, "%ld", count); 1282 break;} 1283 default: 1284 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1285 } 1286} 1287