1234949Sbapt/* 2234949Sbapt * Copyright (c) 1985, 1988 Regents of the University of California. 3234949Sbapt * All rights reserved. 4234949Sbapt * 5234949Sbapt * Redistribution and use in source and binary forms are permitted 6234949Sbapt * provided that the above copyright notice and this paragraph are 7234949Sbapt * duplicated in all such forms and that any documentation, 8234949Sbapt * advertising materials, and other materials related to such 9234949Sbapt * distribution and use acknowledge that the software was developed 10234949Sbapt * by the University of California, Berkeley. The name of the 11234949Sbapt * University may not be used to endorse or promote products derived 12234949Sbapt * from this software without specific prior written permission. 13234949Sbapt * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14234949Sbapt * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15234949Sbapt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16234949Sbapt * 17234949Sbapt * @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89 18234949Sbapt */ 19234949Sbapt 20234949Sbapt/* 21234949Sbapt * Grammar for FTP commands. 22234949Sbapt * See RFC 959. 23234949Sbapt */ 24234949Sbapt 25234949Sbapt%{ 26234949Sbapt 27234949Sbapt/* sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; */ 28234949Sbapt 29234949Sbapt#include <sys/param.h> 30234949Sbapt#include <sys/socket.h> 31234949Sbapt 32234949Sbapt#include <netinet/in.h> 33234949Sbapt 34234949Sbapt#include <arpa/ftp.h> 35234949Sbapt 36234949Sbapt#include <stdlib.h> 37234949Sbapt#include <unistd.h> 38234949Sbapt#include <stdio.h> 39234949Sbapt#include <signal.h> 40234949Sbapt#include <ctype.h> 41234949Sbapt#include <pwd.h> 42234949Sbapt#include <setjmp.h> 43234949Sbapt#include <syslog.h> 44234949Sbapt#include <sys/stat.h> 45234949Sbapt#include <string.h> 46234949Sbapt#include <time.h> 47234949Sbapt#include <assert.h> 48234949Sbapt 49234949Sbapt#ifdef YYBISON 50234949Sbaptint yylex(void); 51234949Sbaptstatic void yyerror(const char *); 52234949Sbapt#endif 53234949Sbapt 54234949Sbaptextern struct sockaddr_in data_dest; 55234949Sbaptextern int logged_in; 56234949Sbaptextern struct passwd *pw; 57234949Sbaptextern int guest; 58234949Sbaptextern int logging; 59234949Sbaptextern int type; 60234949Sbaptextern int form; 61234949Sbaptextern int debug; 62234949Sbaptextern int timeout; 63234949Sbaptextern int maxtimeout; 64234949Sbaptextern int pdata; 65234949Sbaptextern char hostname[], remotehost[]; 66234949Sbaptextern char proctitle[]; 67234949Sbaptextern char *globerr; 68234949Sbaptextern int usedefault; 69234949Sbaptextern int transflag; 70234949Sbaptextern char tmpline[]; 71234949Sbapt 72234949Sbaptextern char **glob(char *); 73234949Sbaptextern char *renamefrom(char *); 74234949Sbaptextern void cwd(const char *); 75234949Sbapt 76234949Sbaptextern void dologout(int); 77234949Sbaptextern void fatal(const char *); 78234949Sbaptextern void makedir(const char *); 79234949Sbaptextern void nack(const char *); 80234949Sbaptextern void pass(const char *); 81234949Sbaptextern void passive(void); 82234949Sbaptextern void pwd(void); 83234949Sbaptextern void removedir(char *); 84234949Sbaptextern void renamecmd(char *, char *); 85234949Sbaptextern void retrieve(const char *, const char *); 86234949Sbaptextern void send_file_list(const char *); 87234949Sbaptextern void statcmd(void); 88234949Sbaptextern void statfilecmd(const char *); 89234949Sbaptextern void store(char *, const char *, int); 90234949Sbaptextern void user(const char *); 91234949Sbapt 92234949Sbaptextern void perror_reply(int, const char *, ...); 93234949Sbaptextern void reply(int, const char *, ...); 94234949Sbaptextern void lreply(int, const char *, ...); 95234949Sbapt 96234949Sbaptstatic int cmd_type; 97234949Sbaptstatic int cmd_form; 98234949Sbaptstatic int cmd_bytesz; 99234949Sbaptchar cbuf[512]; 100234949Sbaptchar *fromname; 101234949Sbapt 102234949Sbaptstruct tab { 103234949Sbapt const char *name; 104234949Sbapt short token; 105234949Sbapt short state; 106234949Sbapt short implemented; /* 1 if command is implemented */ 107234949Sbapt const char *help; 108234949Sbapt}; 109234949Sbapt 110234949Sbaptstatic char * copy(const char *); 111234949Sbapt 112234949Sbapt#ifdef YYBISON 113234949Sbaptstatic void sizecmd(char *filename); 114234949Sbaptstatic void help(struct tab *ctab, char *s); 115234949Sbaptstruct tab cmdtab[]; 116234949Sbaptstruct tab sitetab[]; 117234949Sbapt#endif 118234949Sbapt 119234949Sbaptstatic void 120234949Sbaptyyerror(const char *msg) 121234949Sbapt{ 122234949Sbapt perror(msg); 123234949Sbapt} 124234949Sbapt%} 125234949Sbapt 126251143Sbapt%union 127251143Sbapt{ 128251143Sbapt int ival; 129251143Sbapt char *sval; 130251143Sbapt} 131251143Sbapt%token <ival> NUMBER 132251143Sbapt%token <sval> STRING 133251143Sbapt 134251143Sbapt%type <ival> 135251143Sbapt byte_size 136251143Sbapt check_login 137251143Sbapt form_code 138251143Sbapt mode_code 139251143Sbapt octal_number 140251143Sbapt struct_code 141251143Sbapt 142251143Sbapt%type <sval> 143251143Sbapt password 144251143Sbapt pathname 145251143Sbapt pathstring 146251143Sbapt username 147251143Sbapt 148234949Sbapt%token 149234949Sbapt A B C E F I 150234949Sbapt L N P R S T 151234949Sbapt 152234949Sbapt SP CRLF COMMA STRING NUMBER 153234949Sbapt 154234949Sbapt USER PASS ACCT REIN QUIT PORT 155234949Sbapt PASV TYPE STRU MODE RETR STOR 156234949Sbapt APPE MLFL MAIL MSND MSOM MSAM 157234949Sbapt MRSQ MRCP ALLO REST RNFR RNTO 158234949Sbapt ABOR DELE CWD LIST NLST SITE 159234949Sbapt STAT HELP NOOP MKD RMD PWD 160234949Sbapt CDUP STOU SMNT SYST SIZE MDTM 161234949Sbapt 162234949Sbapt UMASK IDLE CHMOD 163234949Sbapt 164234949Sbapt LEXERR 165234949Sbapt 166234949Sbapt%start cmd_list 167234949Sbapt 168234949Sbapt%% 169234949Sbapt 170234949Sbaptcmd_list: /* empty */ 171234949Sbapt | cmd_list cmd 172234949Sbapt { 173234949Sbapt fromname = (char *) 0; 174234949Sbapt } 175234949Sbapt | cmd_list rcmd 176234949Sbapt ; 177234949Sbapt 178234949Sbaptcmd: USER SP username CRLF 179234949Sbapt { 180251143Sbapt user($3); 181251143Sbapt free($3); 182234949Sbapt } 183234949Sbapt | PASS SP password CRLF 184234949Sbapt { 185251143Sbapt pass($3); 186251143Sbapt free($3); 187234949Sbapt } 188234949Sbapt | PORT SP host_port CRLF 189234949Sbapt { 190234949Sbapt usedefault = 0; 191234949Sbapt if (pdata >= 0) { 192234949Sbapt (void) close(pdata); 193234949Sbapt pdata = -1; 194234949Sbapt } 195234949Sbapt reply(200, "PORT command successful."); 196234949Sbapt } 197234949Sbapt | PASV CRLF 198234949Sbapt { 199234949Sbapt passive(); 200234949Sbapt } 201234949Sbapt | TYPE SP type_code CRLF 202234949Sbapt { 203234949Sbapt switch (cmd_type) { 204234949Sbapt 205234949Sbapt case TYPE_A: 206234949Sbapt if (cmd_form == FORM_N) { 207234949Sbapt reply(200, "Type set to A."); 208234949Sbapt type = cmd_type; 209234949Sbapt form = cmd_form; 210234949Sbapt } else 211234949Sbapt reply(504, "Form must be N."); 212234949Sbapt break; 213234949Sbapt 214234949Sbapt case TYPE_E: 215234949Sbapt reply(504, "Type E not implemented."); 216234949Sbapt break; 217234949Sbapt 218234949Sbapt case TYPE_I: 219234949Sbapt reply(200, "Type set to I."); 220234949Sbapt type = cmd_type; 221234949Sbapt break; 222234949Sbapt 223234949Sbapt case TYPE_L: 224234949Sbapt#if NBBY == 8 225234949Sbapt if (cmd_bytesz == 8) { 226234949Sbapt reply(200, 227234949Sbapt "Type set to L (byte size 8)."); 228234949Sbapt type = cmd_type; 229234949Sbapt } else 230234949Sbapt reply(504, "Byte size must be 8."); 231234949Sbapt#else /* NBBY == 8 */ 232234949Sbapt UNIMPLEMENTED for NBBY != 8 233234949Sbapt#endif /* NBBY == 8 */ 234234949Sbapt } 235234949Sbapt } 236234949Sbapt | STRU SP struct_code CRLF 237234949Sbapt { 238234949Sbapt switch ($3) { 239234949Sbapt 240234949Sbapt case STRU_F: 241234949Sbapt reply(200, "STRU F ok."); 242234949Sbapt break; 243234949Sbapt 244234949Sbapt default: 245234949Sbapt reply(504, "Unimplemented STRU type."); 246234949Sbapt } 247234949Sbapt } 248234949Sbapt | MODE SP mode_code CRLF 249234949Sbapt { 250234949Sbapt switch ($3) { 251234949Sbapt 252234949Sbapt case MODE_S: 253234949Sbapt reply(200, "MODE S ok."); 254234949Sbapt break; 255234949Sbapt 256234949Sbapt default: 257234949Sbapt reply(502, "Unimplemented MODE type."); 258234949Sbapt } 259234949Sbapt } 260234949Sbapt | ALLO SP NUMBER CRLF 261234949Sbapt { 262234949Sbapt reply(202, "ALLO command ignored."); 263234949Sbapt } 264234949Sbapt | ALLO SP NUMBER SP R SP NUMBER CRLF 265234949Sbapt { 266234949Sbapt reply(202, "ALLO command ignored."); 267234949Sbapt } 268234949Sbapt | RETR check_login SP pathname CRLF 269234949Sbapt { 270234949Sbapt if ($2 && $4 != 0) 271251143Sbapt retrieve((char *) 0, $4); 272234949Sbapt if ($4 != 0) 273251143Sbapt free($4); 274234949Sbapt } 275234949Sbapt | STOR check_login SP pathname CRLF 276234949Sbapt { 277234949Sbapt if ($2 && $4 != 0) 278251143Sbapt store($4, "w", 0); 279234949Sbapt if ($4 != 0) 280251143Sbapt free($4); 281234949Sbapt } 282234949Sbapt | APPE check_login SP pathname CRLF 283234949Sbapt { 284234949Sbapt if ($2 && $4 != 0) 285251143Sbapt store($4, "a", 0); 286234949Sbapt if ($4 != 0) 287251143Sbapt free($4); 288234949Sbapt } 289234949Sbapt | NLST check_login CRLF 290234949Sbapt { 291234949Sbapt if ($2) 292234949Sbapt send_file_list("."); 293234949Sbapt } 294234949Sbapt | NLST check_login SP STRING CRLF 295234949Sbapt { 296234949Sbapt if ($2 && $4 != 0) 297234949Sbapt send_file_list((char *) $4); 298234949Sbapt if ($4 != 0) 299234949Sbapt free((char *) $4); 300234949Sbapt } 301234949Sbapt | LIST check_login CRLF 302234949Sbapt { 303234949Sbapt if ($2) 304234949Sbapt retrieve("/bin/ls -lgA", ""); 305234949Sbapt } 306234949Sbapt | LIST check_login SP pathname CRLF 307234949Sbapt { 308234949Sbapt if ($2 && $4 != 0) 309251143Sbapt retrieve("/bin/ls -lgA %s", $4); 310234949Sbapt if ($4 != 0) 311251143Sbapt free($4); 312234949Sbapt } 313234949Sbapt | STAT check_login SP pathname CRLF 314234949Sbapt { 315234949Sbapt if ($2 && $4 != 0) 316251143Sbapt statfilecmd($4); 317234949Sbapt if ($4 != 0) 318251143Sbapt free($4); 319234949Sbapt } 320234949Sbapt | STAT CRLF 321234949Sbapt { 322234949Sbapt statcmd(); 323234949Sbapt } 324234949Sbapt | DELE check_login SP pathname CRLF 325234949Sbapt { 326234949Sbapt if ($2 && $4 != 0) 327234949Sbapt remove((char *) $4); 328234949Sbapt if ($4 != 0) 329234949Sbapt free((char *) $4); 330234949Sbapt } 331234949Sbapt | RNTO SP pathname CRLF 332234949Sbapt { 333234949Sbapt if (fromname) { 334234949Sbapt renamecmd(fromname, (char *) $3); 335234949Sbapt free(fromname); 336234949Sbapt fromname = (char *) 0; 337234949Sbapt } else { 338234949Sbapt reply(503, "Bad sequence of commands."); 339234949Sbapt } 340234949Sbapt free((char *) $3); 341234949Sbapt } 342234949Sbapt | ABOR CRLF 343234949Sbapt { 344234949Sbapt reply(225, "ABOR command successful."); 345234949Sbapt } 346234949Sbapt | CWD check_login CRLF 347234949Sbapt { 348234949Sbapt if ($2) 349234949Sbapt cwd(pw->pw_dir); 350234949Sbapt } 351234949Sbapt | CWD check_login SP pathname CRLF 352234949Sbapt { 353234949Sbapt if ($2 && $4 != 0) 354234949Sbapt cwd((char *) $4); 355234949Sbapt if ($4 != 0) 356234949Sbapt free((char *) $4); 357234949Sbapt } 358234949Sbapt | HELP CRLF 359234949Sbapt { 360234949Sbapt help(cmdtab, (char *) 0); 361234949Sbapt } 362234949Sbapt | HELP SP STRING CRLF 363234949Sbapt { 364234949Sbapt register char *cp = (char *)$3; 365234949Sbapt 366234949Sbapt if (strncasecmp(cp, "SITE", 4) == 0) { 367234949Sbapt cp = (char *)$3 + 4; 368234949Sbapt if (*cp == ' ') 369234949Sbapt cp++; 370234949Sbapt if (*cp) 371234949Sbapt help(sitetab, cp); 372234949Sbapt else 373234949Sbapt help(sitetab, (char *) 0); 374234949Sbapt } else 375234949Sbapt help(cmdtab, (char *) $3); 376234949Sbapt } 377234949Sbapt | NOOP CRLF 378234949Sbapt { 379234949Sbapt reply(200, "NOOP command successful."); 380234949Sbapt } 381234949Sbapt | MKD check_login SP pathname CRLF 382234949Sbapt { 383234949Sbapt if ($2 && $4 != 0) 384234949Sbapt makedir((char *) $4); 385234949Sbapt if ($4 != 0) 386234949Sbapt free((char *) $4); 387234949Sbapt } 388234949Sbapt | RMD check_login SP pathname CRLF 389234949Sbapt { 390234949Sbapt if ($2 && $4 != 0) 391234949Sbapt removedir((char *) $4); 392234949Sbapt if ($4 != 0) 393234949Sbapt free((char *) $4); 394234949Sbapt } 395234949Sbapt | PWD check_login CRLF 396234949Sbapt { 397234949Sbapt if ($2) 398234949Sbapt pwd(); 399234949Sbapt } 400234949Sbapt | CDUP check_login CRLF 401234949Sbapt { 402234949Sbapt if ($2) 403234949Sbapt cwd(".."); 404234949Sbapt } 405234949Sbapt | SITE SP HELP CRLF 406234949Sbapt { 407234949Sbapt help(sitetab, (char *) 0); 408234949Sbapt } 409234949Sbapt | SITE SP HELP SP STRING CRLF 410234949Sbapt { 411234949Sbapt help(sitetab, (char *) $5); 412234949Sbapt } 413234949Sbapt | SITE SP UMASK check_login CRLF 414234949Sbapt { 415234949Sbapt int oldmask; 416234949Sbapt 417234949Sbapt if ($4) { 418234949Sbapt oldmask = umask(0); 419234949Sbapt (void) umask(oldmask); 420234949Sbapt reply(200, "Current UMASK is %03o", oldmask); 421234949Sbapt } 422234949Sbapt } 423234949Sbapt | SITE SP UMASK check_login SP octal_number CRLF 424234949Sbapt { 425234949Sbapt int oldmask; 426234949Sbapt 427234949Sbapt if ($4) { 428234949Sbapt if (($6 == -1) || ($6 > 0777)) { 429234949Sbapt reply(501, "Bad UMASK value"); 430234949Sbapt } else { 431234949Sbapt oldmask = umask($6); 432234949Sbapt reply(200, 433234949Sbapt "UMASK set to %03o (was %03o)", 434234949Sbapt $6, oldmask); 435234949Sbapt } 436234949Sbapt } 437234949Sbapt } 438234949Sbapt | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 439234949Sbapt { 440234949Sbapt if ($4 && ($8 != 0)) { 441234949Sbapt if ($6 > 0777) 442234949Sbapt reply(501, 443234949Sbapt "CHMOD: Mode value must be between 0 and 0777"); 444234949Sbapt else if (chmod((char *) $8, $6) < 0) 445234949Sbapt perror_reply(550, (char *) $8); 446234949Sbapt else 447234949Sbapt reply(200, "CHMOD command successful."); 448234949Sbapt } 449234949Sbapt if ($8 != 0) 450234949Sbapt free((char *) $8); 451234949Sbapt } 452234949Sbapt | SITE SP IDLE CRLF 453234949Sbapt { 454234949Sbapt reply(200, 455234949Sbapt "Current IDLE time limit is %d seconds; max %d", 456234949Sbapt timeout, maxtimeout); 457234949Sbapt } 458234949Sbapt | SITE SP IDLE SP NUMBER CRLF 459234949Sbapt { 460234949Sbapt if ($5 < 30 || $5 > maxtimeout) { 461234949Sbapt reply(501, 462234949Sbapt "Maximum IDLE time must be between 30 and %d seconds", 463234949Sbapt maxtimeout); 464234949Sbapt } else { 465234949Sbapt timeout = $5; 466234949Sbapt (void) alarm((unsigned) timeout); 467234949Sbapt reply(200, 468234949Sbapt "Maximum IDLE time set to %d seconds", 469234949Sbapt timeout); 470234949Sbapt } 471234949Sbapt } 472234949Sbapt | STOU check_login SP pathname CRLF 473234949Sbapt { 474234949Sbapt if ($2 && $4 != 0) 475234949Sbapt store((char *) $4, "w", 1); 476234949Sbapt if ($4 != 0) 477234949Sbapt free((char *) $4); 478234949Sbapt } 479234949Sbapt | SYST CRLF 480234949Sbapt { 481234949Sbapt#ifdef unix 482234949Sbapt#ifdef BSD 483234949Sbapt reply(215, "UNIX Type: L%d Version: BSD-%d", 484234949Sbapt NBBY, BSD); 485234949Sbapt#else /* BSD */ 486234949Sbapt reply(215, "UNIX Type: L%d", NBBY); 487234949Sbapt#endif /* BSD */ 488234949Sbapt#else /* unix */ 489234949Sbapt reply(215, "UNKNOWN Type: L%d", NBBY); 490234949Sbapt#endif /* unix */ 491234949Sbapt } 492234949Sbapt 493234949Sbapt /* 494234949Sbapt * SIZE is not in RFC959, but Postel has blessed it and 495234949Sbapt * it will be in the updated RFC. 496234949Sbapt * 497234949Sbapt * Return size of file in a format suitable for 498234949Sbapt * using with RESTART (we just count bytes). 499234949Sbapt */ 500234949Sbapt | SIZE check_login SP pathname CRLF 501234949Sbapt { 502234949Sbapt if ($2 && $4 != 0) 503234949Sbapt sizecmd((char *) $4); 504234949Sbapt if ($4 != 0) 505234949Sbapt free((char *) $4); 506234949Sbapt } 507234949Sbapt 508234949Sbapt /* 509234949Sbapt * MDTM is not in RFC959, but Postel has blessed it and 510234949Sbapt * it will be in the updated RFC. 511234949Sbapt * 512234949Sbapt * Return modification time of file as an ISO 3307 513234949Sbapt * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 514234949Sbapt * where xxx is the fractional second (of any precision, 515234949Sbapt * not necessarily 3 digits) 516234949Sbapt */ 517234949Sbapt | MDTM check_login SP pathname CRLF 518234949Sbapt { 519234949Sbapt if ($2 && $4 != 0) { 520234949Sbapt struct stat stbuf; 521234949Sbapt if (stat((char *) $4, &stbuf) < 0) 522234949Sbapt perror_reply(550, "%s", (char *) $4); 523234949Sbapt else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 524234949Sbapt reply(550, "%s: not a plain file.", 525234949Sbapt (char *) $4); 526234949Sbapt } else { 527234949Sbapt register struct tm *t; 528234949Sbapt t = gmtime(&stbuf.st_mtime); 529234949Sbapt reply(213, 530234949Sbapt "%04d%02d%02d%02d%02d%02d", 531234949Sbapt 1900 + t->tm_year, 532234949Sbapt t->tm_mon+1, t->tm_mday, 533234949Sbapt t->tm_hour, t->tm_min, t->tm_sec); 534234949Sbapt } 535234949Sbapt } 536234949Sbapt if ($4 != 0) 537234949Sbapt free((char *) $4); 538234949Sbapt } 539234949Sbapt | QUIT CRLF 540234949Sbapt { 541234949Sbapt reply(221, "Goodbye."); 542234949Sbapt dologout(0); 543234949Sbapt } 544234949Sbapt | error CRLF 545234949Sbapt { 546234949Sbapt yyerrok; 547234949Sbapt } 548234949Sbapt ; 549234949Sbaptrcmd: RNFR check_login SP pathname CRLF 550234949Sbapt { 551234949Sbapt if ($2 && $4) { 552234949Sbapt fromname = renamefrom((char *) $4); 553234949Sbapt if (fromname == (char *) 0 && $4) { 554234949Sbapt free((char *) $4); 555234949Sbapt } 556234949Sbapt } 557234949Sbapt } 558234949Sbapt ; 559234949Sbapt 560234949Sbaptusername: STRING 561234949Sbapt ; 562234949Sbapt 563234949Sbaptpassword: /* empty */ 564234949Sbapt { 565234949Sbapt *(const char **)(&($$)) = ""; 566234949Sbapt } 567234949Sbapt | STRING 568234949Sbapt ; 569234949Sbapt 570234949Sbaptbyte_size: NUMBER 571234949Sbapt ; 572234949Sbapt 573234949Sbapthost_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 574234949Sbapt NUMBER COMMA NUMBER 575234949Sbapt { 576234949Sbapt register char *a, *p; 577234949Sbapt 578234949Sbapt a = (char *)&data_dest.sin_addr; 579251143Sbapt a[0] = (char) $1; 580251143Sbapt a[1] = (char) $3; 581251143Sbapt a[2] = (char) $5; 582251143Sbapt a[3] = (char) $7; 583234949Sbapt p = (char *)&data_dest.sin_port; 584251143Sbapt p[0] = (char) $9; 585251143Sbapt p[1] = (char) $11; 586234949Sbapt data_dest.sin_family = AF_INET; 587234949Sbapt } 588234949Sbapt ; 589234949Sbapt 590234949Sbaptform_code: N 591234949Sbapt { 592234949Sbapt $$ = FORM_N; 593234949Sbapt } 594234949Sbapt | T 595234949Sbapt { 596234949Sbapt $$ = FORM_T; 597234949Sbapt } 598234949Sbapt | C 599234949Sbapt { 600234949Sbapt $$ = FORM_C; 601234949Sbapt } 602234949Sbapt ; 603234949Sbapt 604234949Sbapttype_code: A 605234949Sbapt { 606234949Sbapt cmd_type = TYPE_A; 607234949Sbapt cmd_form = FORM_N; 608234949Sbapt } 609234949Sbapt | A SP form_code 610234949Sbapt { 611234949Sbapt cmd_type = TYPE_A; 612234949Sbapt cmd_form = $3; 613234949Sbapt } 614234949Sbapt | E 615234949Sbapt { 616234949Sbapt cmd_type = TYPE_E; 617234949Sbapt cmd_form = FORM_N; 618234949Sbapt } 619234949Sbapt | E SP form_code 620234949Sbapt { 621234949Sbapt cmd_type = TYPE_E; 622234949Sbapt cmd_form = $3; 623234949Sbapt } 624234949Sbapt | I 625234949Sbapt { 626234949Sbapt cmd_type = TYPE_I; 627234949Sbapt } 628234949Sbapt | L 629234949Sbapt { 630234949Sbapt cmd_type = TYPE_L; 631234949Sbapt cmd_bytesz = NBBY; 632234949Sbapt } 633234949Sbapt | L SP byte_size 634234949Sbapt { 635234949Sbapt cmd_type = TYPE_L; 636234949Sbapt cmd_bytesz = $3; 637234949Sbapt } 638234949Sbapt /* this is for a bug in the BBN ftp */ 639234949Sbapt | L byte_size 640234949Sbapt { 641234949Sbapt cmd_type = TYPE_L; 642234949Sbapt cmd_bytesz = $2; 643234949Sbapt } 644234949Sbapt ; 645234949Sbapt 646234949Sbaptstruct_code: F 647234949Sbapt { 648234949Sbapt $$ = STRU_F; 649234949Sbapt } 650234949Sbapt | R 651234949Sbapt { 652234949Sbapt $$ = STRU_R; 653234949Sbapt } 654234949Sbapt | P 655234949Sbapt { 656234949Sbapt $$ = STRU_P; 657234949Sbapt } 658234949Sbapt ; 659234949Sbapt 660234949Sbaptmode_code: S 661234949Sbapt { 662234949Sbapt $$ = MODE_S; 663234949Sbapt } 664234949Sbapt | B 665234949Sbapt { 666234949Sbapt $$ = MODE_B; 667234949Sbapt } 668234949Sbapt | C 669234949Sbapt { 670234949Sbapt $$ = MODE_C; 671234949Sbapt } 672234949Sbapt ; 673234949Sbapt 674234949Sbaptpathname: pathstring 675234949Sbapt { 676234949Sbapt /* 677234949Sbapt * Problem: this production is used for all pathname 678234949Sbapt * processing, but only gives a 550 error reply. 679234949Sbapt * This is a valid reply in some cases but not in others. 680234949Sbapt */ 681234949Sbapt if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 682234949Sbapt *(char **)&($$) = *glob((char *) $1); 683234949Sbapt if (globerr != 0) { 684234949Sbapt reply(550, globerr); 685234949Sbapt $$ = 0; 686234949Sbapt } 687234949Sbapt free((char *) $1); 688234949Sbapt } else 689234949Sbapt $$ = $1; 690234949Sbapt } 691234949Sbapt ; 692234949Sbapt 693234949Sbaptpathstring: STRING 694234949Sbapt ; 695234949Sbapt 696234949Sbaptoctal_number: NUMBER 697234949Sbapt { 698234949Sbapt register int ret, dec, multby, digit; 699234949Sbapt 700234949Sbapt /* 701234949Sbapt * Convert a number that was read as decimal number 702234949Sbapt * to what it would be if it had been read as octal. 703234949Sbapt */ 704234949Sbapt dec = $1; 705234949Sbapt multby = 1; 706234949Sbapt ret = 0; 707234949Sbapt while (dec) { 708234949Sbapt digit = dec%10; 709234949Sbapt if (digit > 7) { 710234949Sbapt ret = -1; 711234949Sbapt break; 712234949Sbapt } 713234949Sbapt ret += digit * multby; 714234949Sbapt multby *= 8; 715234949Sbapt dec /= 10; 716234949Sbapt } 717234949Sbapt $$ = ret; 718234949Sbapt } 719234949Sbapt ; 720234949Sbapt 721234949Sbaptcheck_login: /* empty */ 722234949Sbapt { 723234949Sbapt if (logged_in) 724234949Sbapt $$ = 1; 725234949Sbapt else { 726234949Sbapt reply(530, "Please login with USER and PASS."); 727234949Sbapt $$ = 0; 728234949Sbapt } 729234949Sbapt } 730234949Sbapt ; 731234949Sbapt 732234949Sbapt%% 733234949Sbapt 734234949Sbapt#ifdef YYBYACC 735234949Sbaptextern int YYLEX_DECL(); 736234949Sbapt#endif 737234949Sbapt 738234949Sbaptextern jmp_buf errcatch; 739234949Sbapt 740234949Sbaptstatic void upper(char *); 741234949Sbapt 742234949Sbapt#define CMD 0 /* beginning of command */ 743234949Sbapt#define ARGS 1 /* expect miscellaneous arguments */ 744234949Sbapt#define STR1 2 /* expect SP followed by STRING */ 745234949Sbapt#define STR2 3 /* expect STRING */ 746234949Sbapt#define OSTR 4 /* optional SP then STRING */ 747234949Sbapt#define ZSTR1 5 /* SP then optional STRING */ 748234949Sbapt#define ZSTR2 6 /* optional STRING after SP */ 749234949Sbapt#define SITECMD 7 /* SITE command */ 750234949Sbapt#define NSTR 8 /* Number followed by a string */ 751234949Sbapt 752234949Sbaptstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 753234949Sbapt { "USER", USER, STR1, 1, "<sp> username" }, 754234949Sbapt { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 755234949Sbapt { "ACCT", ACCT, STR1, 0, "(specify account)" }, 756234949Sbapt { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 757234949Sbapt { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 758234949Sbapt { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 759234949Sbapt { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 760234949Sbapt { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 761234949Sbapt { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 762234949Sbapt { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 763234949Sbapt { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 764234949Sbapt { "RETR", RETR, STR1, 1, "<sp> file-name" }, 765234949Sbapt { "STOR", STOR, STR1, 1, "<sp> file-name" }, 766234949Sbapt { "APPE", APPE, STR1, 1, "<sp> file-name" }, 767234949Sbapt { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 768234949Sbapt { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 769234949Sbapt { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 770234949Sbapt { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 771234949Sbapt { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 772234949Sbapt { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 773234949Sbapt { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 774234949Sbapt { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 775234949Sbapt { "REST", REST, ARGS, 0, "(restart command)" }, 776234949Sbapt { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 777234949Sbapt { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 778234949Sbapt { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 779234949Sbapt { "DELE", DELE, STR1, 1, "<sp> file-name" }, 780234949Sbapt { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 781234949Sbapt { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 782234949Sbapt { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 783234949Sbapt { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 784234949Sbapt { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 785234949Sbapt { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 786234949Sbapt { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 787234949Sbapt { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 788234949Sbapt { "NOOP", NOOP, ARGS, 1, "" }, 789234949Sbapt { "MKD", MKD, STR1, 1, "<sp> path-name" }, 790234949Sbapt { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 791234949Sbapt { "RMD", RMD, STR1, 1, "<sp> path-name" }, 792234949Sbapt { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 793234949Sbapt { "PWD", PWD, ARGS, 1, "(return current directory)" }, 794234949Sbapt { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 795234949Sbapt { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 796234949Sbapt { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 797234949Sbapt { "STOU", STOU, STR1, 1, "<sp> file-name" }, 798234949Sbapt { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 799234949Sbapt { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 800234949Sbapt { 0, 0, 0, 0, 0 } 801234949Sbapt}; 802234949Sbapt 803234949Sbaptstruct tab sitetab[] = { 804234949Sbapt { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 805234949Sbapt { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 806234949Sbapt { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 807234949Sbapt { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 808234949Sbapt { 0, 0, 0, 0, 0 } 809234949Sbapt}; 810234949Sbapt 811234949Sbaptstatic struct tab * 812234949Sbaptlookup(struct tab *p, char *cmd) 813234949Sbapt{ 814234949Sbapt 815234949Sbapt for (; p->name != 0; p++) 816234949Sbapt if (strcmp(cmd, p->name) == 0) 817234949Sbapt return (p); 818234949Sbapt return (0); 819234949Sbapt} 820234949Sbapt 821234949Sbapt#include <arpa/telnet.h> 822234949Sbapt 823234949Sbapt/* 824234949Sbapt * get_line - a hacked up version of fgets to ignore TELNET escape codes. 825234949Sbapt */ 826234949Sbaptstatic char * 827234949Sbaptget_line(char *s, int n, FILE *iop) 828234949Sbapt{ 829234949Sbapt register int c; 830234949Sbapt register char *cs; 831234949Sbapt 832234949Sbapt cs = s; 833234949Sbapt/* tmpline may contain saved command from urgent mode interruption */ 834234949Sbapt for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 835234949Sbapt *cs++ = tmpline[c]; 836234949Sbapt if (tmpline[c] == '\n') { 837234949Sbapt *cs = '\0'; 838234949Sbapt if (debug) 839234949Sbapt syslog(LOG_DEBUG, "command: %s", s); 840234949Sbapt tmpline[0] = '\0'; 841234949Sbapt return(s); 842234949Sbapt } 843234949Sbapt if (c == 0) 844234949Sbapt tmpline[0] = '\0'; 845234949Sbapt } 846234949Sbapt while ((c = getc(iop)) != EOF) { 847234949Sbapt c &= 0377; 848234949Sbapt if (c == IAC) { 849234949Sbapt if ((c = getc(iop)) != EOF) { 850234949Sbapt c &= 0377; 851234949Sbapt switch (c) { 852234949Sbapt case WILL: 853234949Sbapt case WONT: 854234949Sbapt c = getc(iop); 855234949Sbapt printf("%c%c%c", IAC, DONT, 0377&c); 856234949Sbapt (void) fflush(stdout); 857234949Sbapt continue; 858234949Sbapt case DO: 859234949Sbapt case DONT: 860234949Sbapt c = getc(iop); 861234949Sbapt printf("%c%c%c", IAC, WONT, 0377&c); 862234949Sbapt (void) fflush(stdout); 863234949Sbapt continue; 864234949Sbapt case IAC: 865234949Sbapt break; 866234949Sbapt default: 867234949Sbapt continue; /* ignore command */ 868234949Sbapt } 869234949Sbapt } 870234949Sbapt } 871251143Sbapt *cs++ = (char) c; 872234949Sbapt if (--n <= 0 || c == '\n') 873234949Sbapt break; 874234949Sbapt } 875234949Sbapt if (c == EOF && cs == s) 876234949Sbapt return (0); 877234949Sbapt *cs = '\0'; 878234949Sbapt if (debug) 879234949Sbapt syslog(LOG_DEBUG, "command: %s", s); 880234949Sbapt return (s); 881234949Sbapt} 882234949Sbapt 883234949Sbaptstatic void 884234949Sbapttoolong(int sig) 885234949Sbapt{ 886234949Sbapt time_t now; 887234949Sbapt 888234949Sbapt (void) sig; 889234949Sbapt reply(421, 890234949Sbapt "Timeout (%d seconds): closing control connection.", timeout); 891234949Sbapt (void) time(&now); 892234949Sbapt if (logging) { 893234949Sbapt syslog(LOG_INFO, 894234949Sbapt "User %s timed out after %d seconds at %s", 895234949Sbapt (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 896234949Sbapt } 897234949Sbapt dologout(1); 898234949Sbapt} 899234949Sbapt 900234949Sbaptint 901234949Sbaptyylex(void) 902234949Sbapt{ 903234949Sbapt static int cpos, state; 904234949Sbapt register char *cp, *cp2; 905234949Sbapt register struct tab *p; 906234949Sbapt int n; 907234949Sbapt char c; 908234949Sbapt 909234949Sbapt for (;;) { 910234949Sbapt switch (state) { 911234949Sbapt 912234949Sbapt case CMD: 913234949Sbapt (void) signal(SIGALRM, toolong); 914234949Sbapt (void) alarm((unsigned) timeout); 915234949Sbapt if (get_line(cbuf, sizeof(cbuf)-1, stdin) == 0) { 916234949Sbapt reply(221, "You could at least say goodbye."); 917234949Sbapt dologout(0); 918234949Sbapt } 919234949Sbapt (void) alarm(0); 920234949Sbapt#ifdef SETPROCTITLE 921234949Sbapt if (strncasecmp(cbuf, "PASS", 4) != 0) 922234949Sbapt setproctitle("%s: %s", proctitle, cbuf); 923234949Sbapt#endif /* SETPROCTITLE */ 924234949Sbapt if ((cp = strchr(cbuf, '\r'))) { 925234949Sbapt *cp++ = '\n'; 926234949Sbapt *cp = '\0'; 927234949Sbapt } 928234949Sbapt if ((cp = strpbrk(cbuf, " \n"))) 929251143Sbapt cpos = (int) (cp - cbuf); 930234949Sbapt if (cpos == 0) 931234949Sbapt cpos = 4; 932234949Sbapt c = cbuf[cpos]; 933234949Sbapt cbuf[cpos] = '\0'; 934234949Sbapt upper(cbuf); 935234949Sbapt p = lookup(cmdtab, cbuf); 936234949Sbapt cbuf[cpos] = c; 937234949Sbapt if (p != 0) { 938234949Sbapt if (p->implemented == 0) { 939234949Sbapt nack(p->name); 940234949Sbapt longjmp(errcatch,0); 941234949Sbapt /* NOTREACHED */ 942234949Sbapt } 943234949Sbapt state = p->state; 944234949Sbapt *(const char **)(&yylval) = p->name; 945234949Sbapt return (p->token); 946234949Sbapt } 947234949Sbapt break; 948234949Sbapt 949234949Sbapt case SITECMD: 950234949Sbapt if (cbuf[cpos] == ' ') { 951234949Sbapt cpos++; 952234949Sbapt return (SP); 953234949Sbapt } 954234949Sbapt cp = &cbuf[cpos]; 955234949Sbapt if ((cp2 = strpbrk(cp, " \n"))) 956251143Sbapt cpos = (int) (cp2 - cbuf); 957234949Sbapt c = cbuf[cpos]; 958234949Sbapt cbuf[cpos] = '\0'; 959234949Sbapt upper(cp); 960234949Sbapt p = lookup(sitetab, cp); 961234949Sbapt cbuf[cpos] = c; 962234949Sbapt if (p != 0) { 963234949Sbapt if (p->implemented == 0) { 964234949Sbapt state = CMD; 965234949Sbapt nack(p->name); 966234949Sbapt longjmp(errcatch,0); 967234949Sbapt /* NOTREACHED */ 968234949Sbapt } 969234949Sbapt state = p->state; 970234949Sbapt *(const char **)(&yylval) = p->name; 971234949Sbapt return (p->token); 972234949Sbapt } 973234949Sbapt state = CMD; 974234949Sbapt break; 975234949Sbapt 976234949Sbapt case OSTR: 977234949Sbapt if (cbuf[cpos] == '\n') { 978234949Sbapt state = CMD; 979234949Sbapt return (CRLF); 980234949Sbapt } 981234949Sbapt /* FALLTHROUGH */ 982234949Sbapt 983234949Sbapt case STR1: 984234949Sbapt case ZSTR1: 985234949Sbapt dostr1: 986234949Sbapt if (cbuf[cpos] == ' ') { 987234949Sbapt cpos++; 988234949Sbapt if (state == OSTR) 989234949Sbapt state = STR2; 990234949Sbapt else 991234949Sbapt ++state; 992234949Sbapt return (SP); 993234949Sbapt } 994234949Sbapt break; 995234949Sbapt 996234949Sbapt case ZSTR2: 997234949Sbapt if (cbuf[cpos] == '\n') { 998234949Sbapt state = CMD; 999234949Sbapt return (CRLF); 1000234949Sbapt } 1001234949Sbapt /* FALLTHROUGH */ 1002234949Sbapt 1003234949Sbapt case STR2: 1004234949Sbapt cp = &cbuf[cpos]; 1005251143Sbapt n = (int) strlen(cp); 1006234949Sbapt cpos += n - 1; 1007234949Sbapt /* 1008234949Sbapt * Make sure the string is nonempty and \n terminated. 1009234949Sbapt */ 1010234949Sbapt if (n > 1 && cbuf[cpos] == '\n') { 1011234949Sbapt cbuf[cpos] = '\0'; 1012234949Sbapt *(char **)&yylval = copy(cp); 1013234949Sbapt cbuf[cpos] = '\n'; 1014234949Sbapt state = ARGS; 1015234949Sbapt return (STRING); 1016234949Sbapt } 1017234949Sbapt break; 1018234949Sbapt 1019234949Sbapt case NSTR: 1020234949Sbapt if (cbuf[cpos] == ' ') { 1021234949Sbapt cpos++; 1022234949Sbapt return (SP); 1023234949Sbapt } 1024234949Sbapt if (isdigit(cbuf[cpos])) { 1025234949Sbapt cp = &cbuf[cpos]; 1026234949Sbapt while (isdigit(cbuf[++cpos])) 1027234949Sbapt ; 1028234949Sbapt c = cbuf[cpos]; 1029234949Sbapt cbuf[cpos] = '\0'; 1030251143Sbapt yylval.ival = atoi(cp); 1031234949Sbapt cbuf[cpos] = c; 1032234949Sbapt state = STR1; 1033234949Sbapt return (NUMBER); 1034234949Sbapt } 1035234949Sbapt state = STR1; 1036234949Sbapt goto dostr1; 1037234949Sbapt 1038234949Sbapt case ARGS: 1039234949Sbapt if (isdigit(cbuf[cpos])) { 1040234949Sbapt cp = &cbuf[cpos]; 1041234949Sbapt while (isdigit(cbuf[++cpos])) 1042234949Sbapt ; 1043234949Sbapt c = cbuf[cpos]; 1044234949Sbapt cbuf[cpos] = '\0'; 1045251143Sbapt yylval.ival = atoi(cp); 1046234949Sbapt cbuf[cpos] = c; 1047234949Sbapt return (NUMBER); 1048234949Sbapt } 1049234949Sbapt switch (cbuf[cpos++]) { 1050234949Sbapt 1051234949Sbapt case '\n': 1052234949Sbapt state = CMD; 1053234949Sbapt return (CRLF); 1054234949Sbapt 1055234949Sbapt case ' ': 1056234949Sbapt return (SP); 1057234949Sbapt 1058234949Sbapt case ',': 1059234949Sbapt return (COMMA); 1060234949Sbapt 1061234949Sbapt case 'A': 1062234949Sbapt case 'a': 1063234949Sbapt return (A); 1064234949Sbapt 1065234949Sbapt case 'B': 1066234949Sbapt case 'b': 1067234949Sbapt return (B); 1068234949Sbapt 1069234949Sbapt case 'C': 1070234949Sbapt case 'c': 1071234949Sbapt return (C); 1072234949Sbapt 1073234949Sbapt case 'E': 1074234949Sbapt case 'e': 1075234949Sbapt return (E); 1076234949Sbapt 1077234949Sbapt case 'F': 1078234949Sbapt case 'f': 1079234949Sbapt return (F); 1080234949Sbapt 1081234949Sbapt case 'I': 1082234949Sbapt case 'i': 1083234949Sbapt return (I); 1084234949Sbapt 1085234949Sbapt case 'L': 1086234949Sbapt case 'l': 1087234949Sbapt return (L); 1088234949Sbapt 1089234949Sbapt case 'N': 1090234949Sbapt case 'n': 1091234949Sbapt return (N); 1092234949Sbapt 1093234949Sbapt case 'P': 1094234949Sbapt case 'p': 1095234949Sbapt return (P); 1096234949Sbapt 1097234949Sbapt case 'R': 1098234949Sbapt case 'r': 1099234949Sbapt return (R); 1100234949Sbapt 1101234949Sbapt case 'S': 1102234949Sbapt case 's': 1103234949Sbapt return (S); 1104234949Sbapt 1105234949Sbapt case 'T': 1106234949Sbapt case 't': 1107234949Sbapt return (T); 1108234949Sbapt 1109234949Sbapt } 1110234949Sbapt break; 1111234949Sbapt 1112234949Sbapt default: 1113234949Sbapt fatal("Unknown state in scanner."); 1114234949Sbapt } 1115234949Sbapt yyerror((char *) 0); 1116234949Sbapt state = CMD; 1117234949Sbapt longjmp(errcatch,0); 1118234949Sbapt } 1119234949Sbapt} 1120234949Sbapt 1121234949Sbaptstatic void 1122234949Sbaptupper(char *s) 1123234949Sbapt{ 1124234949Sbapt while (*s != '\0') { 1125234949Sbapt if (islower(*s)) 1126234949Sbapt *s = toupper(*s); 1127234949Sbapt s++; 1128234949Sbapt } 1129234949Sbapt} 1130234949Sbapt 1131234949Sbaptstatic char * 1132234949Sbaptcopy(const char *s) 1133234949Sbapt{ 1134234949Sbapt char *p; 1135234949Sbapt 1136234949Sbapt p = (char * )malloc(strlen(s) + 1); 1137234949Sbapt if (p == 0) 1138234949Sbapt fatal("Ran out of memory."); 1139234949Sbapt else 1140234949Sbapt (void) strcpy(p, s); 1141234949Sbapt return (p); 1142234949Sbapt} 1143234949Sbapt 1144234949Sbaptstatic void 1145234949Sbapthelp(struct tab *ctab, char *s) 1146234949Sbapt{ 1147234949Sbapt register struct tab *c; 1148234949Sbapt register int width, NCMDS; 1149234949Sbapt const char *help_type; 1150234949Sbapt 1151234949Sbapt if (ctab == sitetab) 1152234949Sbapt help_type = "SITE "; 1153234949Sbapt else 1154234949Sbapt help_type = ""; 1155234949Sbapt width = 0, NCMDS = 0; 1156234949Sbapt for (c = ctab; c->name != 0; c++) { 1157251143Sbapt int len = (int) strlen(c->name); 1158234949Sbapt 1159234949Sbapt if (len > width) 1160234949Sbapt width = len; 1161234949Sbapt NCMDS++; 1162234949Sbapt } 1163234949Sbapt width = (width + 8) &~ 7; 1164234949Sbapt if (s == 0) { 1165234949Sbapt register int i, j, w; 1166234949Sbapt int columns, lines; 1167234949Sbapt 1168234949Sbapt lreply(214, "The following %scommands are recognized %s.", 1169234949Sbapt help_type, "(* =>'s unimplemented)"); 1170234949Sbapt columns = 76 / width; 1171234949Sbapt if (columns == 0) 1172234949Sbapt columns = 1; 1173234949Sbapt lines = (NCMDS + columns - 1) / columns; 1174234949Sbapt for (i = 0; i < lines; i++) { 1175234949Sbapt printf(" "); 1176234949Sbapt for (j = 0; j < columns; j++) { 1177234949Sbapt c = ctab + j * lines + i; 1178234949Sbapt assert(c->name != 0); 1179234949Sbapt printf("%s%c", c->name, 1180234949Sbapt c->implemented ? ' ' : '*'); 1181234949Sbapt if (c + lines >= &ctab[NCMDS]) 1182234949Sbapt break; 1183251143Sbapt w = (int) strlen(c->name) + 1; 1184234949Sbapt while (w < width) { 1185234949Sbapt putchar(' '); 1186234949Sbapt w++; 1187234949Sbapt } 1188234949Sbapt } 1189234949Sbapt printf("\r\n"); 1190234949Sbapt } 1191234949Sbapt (void) fflush(stdout); 1192234949Sbapt reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1193234949Sbapt return; 1194234949Sbapt } 1195234949Sbapt upper(s); 1196234949Sbapt c = lookup(ctab, s); 1197234949Sbapt if (c == (struct tab *)0) { 1198234949Sbapt reply(502, "Unknown command %s.", s); 1199234949Sbapt return; 1200234949Sbapt } 1201234949Sbapt if (c->implemented) 1202234949Sbapt reply(214, "Syntax: %s%s %s", help_type, c->name, c->help); 1203234949Sbapt else 1204234949Sbapt reply(214, "%s%-*s\t%s; unimplemented.", help_type, width, 1205234949Sbapt c->name, c->help); 1206234949Sbapt} 1207234949Sbapt 1208234949Sbaptstatic void 1209234949Sbaptsizecmd(char *filename) 1210234949Sbapt{ 1211234949Sbapt switch (type) { 1212234949Sbapt case TYPE_L: 1213234949Sbapt case TYPE_I: { 1214234949Sbapt struct stat stbuf; 1215234949Sbapt if (stat(filename, &stbuf) < 0 || 1216234949Sbapt (stbuf.st_mode&S_IFMT) != S_IFREG) 1217234949Sbapt reply(550, "%s: not a plain file.", filename); 1218234949Sbapt else 1219234949Sbapt#ifdef HAVE_LONG_LONG 1220234949Sbapt reply(213, "%llu", (long long) stbuf.st_size); 1221234949Sbapt#else 1222234949Sbapt reply(213, "%lu", stbuf.st_size); 1223234949Sbapt#endif 1224234949Sbapt break;} 1225234949Sbapt case TYPE_A: { 1226234949Sbapt FILE *fin; 1227234949Sbapt register int c, count; 1228234949Sbapt struct stat stbuf; 1229234949Sbapt fin = fopen(filename, "r"); 1230234949Sbapt if (fin == 0) { 1231234949Sbapt perror_reply(550, filename); 1232234949Sbapt return; 1233234949Sbapt } 1234234949Sbapt if (fstat(fileno(fin), &stbuf) < 0 || 1235234949Sbapt (stbuf.st_mode&S_IFMT) != S_IFREG) { 1236234949Sbapt reply(550, "%s: not a plain file.", filename); 1237234949Sbapt (void) fclose(fin); 1238234949Sbapt return; 1239234949Sbapt } 1240234949Sbapt 1241234949Sbapt count = 0; 1242234949Sbapt while((c=getc(fin)) != EOF) { 1243234949Sbapt if (c == '\n') /* will get expanded to \r\n */ 1244234949Sbapt count++; 1245234949Sbapt count++; 1246234949Sbapt } 1247234949Sbapt (void) fclose(fin); 1248234949Sbapt 1249234949Sbapt reply(213, "%ld", count); 1250234949Sbapt break;} 1251234949Sbapt default: 1252234949Sbapt reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1253234949Sbapt } 1254234949Sbapt} 1255