11590Srgrimes/* 21590Srgrimes * Copyright (c) 1985, 1988 Regents of the University of California. 31590Srgrimes * All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms are permitted 61590Srgrimes * provided that the above copyright notice and this paragraph are 71590Srgrimes * duplicated in all such forms and that any documentation, 81590Srgrimes * advertising materials, and other materials related to such 91590Srgrimes * distribution and use acknowledge that the software was developed 101590Srgrimes * by the University of California, Berkeley. The name of the 111590Srgrimes * University may not be used to endorse or promote products derived 121590Srgrimes * from this software without specific prior written permission. 131590Srgrimes * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 141590Srgrimes * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 151590Srgrimes * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 161590Srgrimes * 171590Srgrimes * @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89 181590Srgrimes */ 191590Srgrimes 201590Srgrimes/* 211590Srgrimes * Grammar for FTP commands. 221590Srgrimes * See RFC 959. 231590Srgrimes */ 241590Srgrimes 251590Srgrimes%{ 261590Srgrimes 271590Srgrimes#ifndef lint 281590Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; 291590Srgrimes#endif /* not lint */ 301590Srgrimes 311590Srgrimes#include <sys/param.h> 321590Srgrimes#include <sys/socket.h> 331590Srgrimes 341590Srgrimes#include <netinet/in.h> 351590Srgrimes 361590Srgrimes#include <arpa/ftp.h> 371590Srgrimes 381590Srgrimes#include <stdio.h> 391590Srgrimes#include <signal.h> 401590Srgrimes#include <ctype.h> 411590Srgrimes#include <pwd.h> 421590Srgrimes#include <setjmp.h> 431590Srgrimes#include <syslog.h> 441590Srgrimes#include <sys/stat.h> 451590Srgrimes#include <time.h> 461590Srgrimes 471590Srgrimesextern struct sockaddr_in data_dest; 481590Srgrimesextern int logged_in; 491590Srgrimesextern struct passwd *pw; 501590Srgrimesextern int guest; 511590Srgrimesextern int logging; 521590Srgrimesextern int type; 531590Srgrimesextern int form; 541590Srgrimesextern int debug; 551590Srgrimesextern int timeout; 561590Srgrimesextern int maxtimeout; 571590Srgrimesextern int pdata; 581590Srgrimesextern char hostname[], remotehost[]; 591590Srgrimesextern char proctitle[]; 601590Srgrimesextern char *globerr; 611590Srgrimesextern int usedefault; 621590Srgrimesextern int transflag; 631590Srgrimesextern char tmpline[]; 641590Srgrimeschar **glob(); 651590Srgrimes 661590Srgrimesstatic int cmd_type; 671590Srgrimesstatic int cmd_form; 681590Srgrimesstatic int cmd_bytesz; 691590Srgrimeschar cbuf[512]; 701590Srgrimeschar *fromname; 711590Srgrimes 721590Srgrimeschar *index(); 731590Srgrimes%} 741590Srgrimes 751590Srgrimes%token 761590Srgrimes A B C E F I 771590Srgrimes L N P R S T 781590Srgrimes 791590Srgrimes SP CRLF COMMA STRING NUMBER 801590Srgrimes 811590Srgrimes USER PASS ACCT REIN QUIT PORT 821590Srgrimes PASV TYPE STRU MODE RETR STOR 831590Srgrimes APPE MLFL MAIL MSND MSOM MSAM 841590Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 851590Srgrimes ABOR DELE CWD LIST NLST SITE 861590Srgrimes STAT HELP NOOP MKD RMD PWD 871590Srgrimes CDUP STOU SMNT SYST SIZE MDTM 881590Srgrimes 891590Srgrimes UMASK IDLE CHMOD 901590Srgrimes 911590Srgrimes LEXERR 921590Srgrimes 931590Srgrimes%start cmd_list 941590Srgrimes 951590Srgrimes%% 961590Srgrimes 971590Srgrimescmd_list: /* empty */ 981590Srgrimes | cmd_list cmd 991590Srgrimes = { 1001590Srgrimes fromname = (char *) 0; 1011590Srgrimes } 1021590Srgrimes | cmd_list rcmd 1031590Srgrimes ; 1041590Srgrimes 1051590Srgrimescmd: USER SP username CRLF 1061590Srgrimes = { 1071590Srgrimes user((char *) $3); 1081590Srgrimes free((char *) $3); 1091590Srgrimes } 1101590Srgrimes | PASS SP password CRLF 1111590Srgrimes = { 1121590Srgrimes pass((char *) $3); 1131590Srgrimes free((char *) $3); 1141590Srgrimes } 1151590Srgrimes | PORT SP host_port CRLF 1161590Srgrimes = { 1171590Srgrimes usedefault = 0; 1181590Srgrimes if (pdata >= 0) { 1191590Srgrimes (void) close(pdata); 1201590Srgrimes pdata = -1; 1211590Srgrimes } 1221590Srgrimes reply(200, "PORT command successful."); 1231590Srgrimes } 1241590Srgrimes | PASV CRLF 1251590Srgrimes = { 1261590Srgrimes passive(); 1271590Srgrimes } 1281590Srgrimes | TYPE SP type_code CRLF 1291590Srgrimes = { 1301590Srgrimes switch (cmd_type) { 1311590Srgrimes 1321590Srgrimes case TYPE_A: 1331590Srgrimes if (cmd_form == FORM_N) { 1341590Srgrimes reply(200, "Type set to A."); 1351590Srgrimes type = cmd_type; 1361590Srgrimes form = cmd_form; 1371590Srgrimes } else 1381590Srgrimes reply(504, "Form must be N."); 1391590Srgrimes break; 1401590Srgrimes 1411590Srgrimes case TYPE_E: 1421590Srgrimes reply(504, "Type E not implemented."); 1431590Srgrimes break; 1441590Srgrimes 1451590Srgrimes case TYPE_I: 1461590Srgrimes reply(200, "Type set to I."); 1471590Srgrimes type = cmd_type; 1481590Srgrimes break; 1491590Srgrimes 1501590Srgrimes case TYPE_L: 1511590Srgrimes#if NBBY == 8 1521590Srgrimes if (cmd_bytesz == 8) { 1531590Srgrimes reply(200, 1541590Srgrimes "Type set to L (byte size 8)."); 1551590Srgrimes type = cmd_type; 1561590Srgrimes } else 1571590Srgrimes reply(504, "Byte size must be 8."); 1581590Srgrimes#else /* NBBY == 8 */ 1591590Srgrimes UNIMPLEMENTED for NBBY != 8 1601590Srgrimes#endif /* NBBY == 8 */ 1611590Srgrimes } 1621590Srgrimes } 1631590Srgrimes | STRU SP struct_code CRLF 1641590Srgrimes = { 1651590Srgrimes switch ($3) { 1661590Srgrimes 1671590Srgrimes case STRU_F: 1681590Srgrimes reply(200, "STRU F ok."); 1691590Srgrimes break; 1701590Srgrimes 1711590Srgrimes default: 1721590Srgrimes reply(504, "Unimplemented STRU type."); 1731590Srgrimes } 1741590Srgrimes } 1751590Srgrimes | MODE SP mode_code CRLF 1761590Srgrimes = { 1771590Srgrimes switch ($3) { 1781590Srgrimes 1791590Srgrimes case MODE_S: 1801590Srgrimes reply(200, "MODE S ok."); 1811590Srgrimes break; 1821590Srgrimes 1831590Srgrimes default: 1841590Srgrimes reply(502, "Unimplemented MODE type."); 1851590Srgrimes } 1861590Srgrimes } 1871590Srgrimes | ALLO SP NUMBER CRLF 1881590Srgrimes = { 1891590Srgrimes reply(202, "ALLO command ignored."); 1901590Srgrimes } 1911590Srgrimes | ALLO SP NUMBER SP R SP NUMBER CRLF 1921590Srgrimes = { 1931590Srgrimes reply(202, "ALLO command ignored."); 1941590Srgrimes } 1951590Srgrimes | RETR check_login SP pathname CRLF 1961590Srgrimes = { 1971590Srgrimes if ($2 && $4 != NULL) 1981590Srgrimes retrieve((char *) 0, (char *) $4); 1991590Srgrimes if ($4 != NULL) 2001590Srgrimes free((char *) $4); 2011590Srgrimes } 2021590Srgrimes | STOR check_login SP pathname CRLF 2031590Srgrimes = { 2041590Srgrimes if ($2 && $4 != NULL) 2051590Srgrimes store((char *) $4, "w", 0); 2061590Srgrimes if ($4 != NULL) 2071590Srgrimes free((char *) $4); 2081590Srgrimes } 2091590Srgrimes | APPE check_login SP pathname CRLF 2101590Srgrimes = { 2111590Srgrimes if ($2 && $4 != NULL) 2121590Srgrimes store((char *) $4, "a", 0); 2131590Srgrimes if ($4 != NULL) 2141590Srgrimes free((char *) $4); 2151590Srgrimes } 2161590Srgrimes | NLST check_login CRLF 2171590Srgrimes = { 2181590Srgrimes if ($2) 2191590Srgrimes send_file_list("."); 2201590Srgrimes } 2211590Srgrimes | NLST check_login SP STRING CRLF 2221590Srgrimes = { 2231590Srgrimes if ($2 && $4 != NULL) 2241590Srgrimes send_file_list((char *) $4); 2251590Srgrimes if ($4 != NULL) 2261590Srgrimes free((char *) $4); 2271590Srgrimes } 2281590Srgrimes | LIST check_login CRLF 2291590Srgrimes = { 2301590Srgrimes if ($2) 2311590Srgrimes retrieve("/bin/ls -lgA", ""); 2321590Srgrimes } 2331590Srgrimes | LIST check_login SP pathname CRLF 2341590Srgrimes = { 2351590Srgrimes if ($2 && $4 != NULL) 2361590Srgrimes retrieve("/bin/ls -lgA %s", (char *) $4); 2371590Srgrimes if ($4 != NULL) 2381590Srgrimes free((char *) $4); 2391590Srgrimes } 2401590Srgrimes | STAT check_login SP pathname CRLF 2411590Srgrimes = { 2421590Srgrimes if ($2 && $4 != NULL) 2431590Srgrimes statfilecmd((char *) $4); 2441590Srgrimes if ($4 != NULL) 2451590Srgrimes free((char *) $4); 2461590Srgrimes } 2471590Srgrimes | STAT CRLF 2481590Srgrimes = { 2491590Srgrimes statcmd(); 2501590Srgrimes } 2511590Srgrimes | DELE check_login SP pathname CRLF 2521590Srgrimes = { 2531590Srgrimes if ($2 && $4 != NULL) 2541590Srgrimes delete((char *) $4); 2551590Srgrimes if ($4 != NULL) 2561590Srgrimes free((char *) $4); 2571590Srgrimes } 2581590Srgrimes | RNTO SP pathname CRLF 2591590Srgrimes = { 2601590Srgrimes if (fromname) { 2611590Srgrimes renamecmd(fromname, (char *) $3); 2621590Srgrimes free(fromname); 2631590Srgrimes fromname = (char *) 0; 2641590Srgrimes } else { 2651590Srgrimes reply(503, "Bad sequence of commands."); 2661590Srgrimes } 2671590Srgrimes free((char *) $3); 2681590Srgrimes } 2691590Srgrimes | ABOR CRLF 2701590Srgrimes = { 2711590Srgrimes reply(225, "ABOR command successful."); 2721590Srgrimes } 2731590Srgrimes | CWD check_login CRLF 2741590Srgrimes = { 2751590Srgrimes if ($2) 2761590Srgrimes cwd(pw->pw_dir); 2771590Srgrimes } 2781590Srgrimes | CWD check_login SP pathname CRLF 2791590Srgrimes = { 2801590Srgrimes if ($2 && $4 != NULL) 2811590Srgrimes cwd((char *) $4); 2821590Srgrimes if ($4 != NULL) 2831590Srgrimes free((char *) $4); 2841590Srgrimes } 2851590Srgrimes | HELP CRLF 2861590Srgrimes = { 2871590Srgrimes help(cmdtab, (char *) 0); 2881590Srgrimes } 2891590Srgrimes | HELP SP STRING CRLF 2901590Srgrimes = { 2911590Srgrimes register char *cp = (char *)$3; 2921590Srgrimes 2931590Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 2941590Srgrimes cp = (char *)$3 + 4; 2951590Srgrimes if (*cp == ' ') 2961590Srgrimes cp++; 2971590Srgrimes if (*cp) 2981590Srgrimes help(sitetab, cp); 2991590Srgrimes else 3001590Srgrimes help(sitetab, (char *) 0); 3011590Srgrimes } else 3021590Srgrimes help(cmdtab, (char *) $3); 3031590Srgrimes } 3041590Srgrimes | NOOP CRLF 3051590Srgrimes = { 3061590Srgrimes reply(200, "NOOP command successful."); 3071590Srgrimes } 3081590Srgrimes | MKD check_login SP pathname CRLF 3091590Srgrimes = { 3101590Srgrimes if ($2 && $4 != NULL) 3111590Srgrimes makedir((char *) $4); 3121590Srgrimes if ($4 != NULL) 3131590Srgrimes free((char *) $4); 3141590Srgrimes } 3151590Srgrimes | RMD check_login SP pathname CRLF 3161590Srgrimes = { 3171590Srgrimes if ($2 && $4 != NULL) 3181590Srgrimes removedir((char *) $4); 3191590Srgrimes if ($4 != NULL) 3201590Srgrimes free((char *) $4); 3211590Srgrimes } 3221590Srgrimes | PWD check_login CRLF 3231590Srgrimes = { 3241590Srgrimes if ($2) 3251590Srgrimes pwd(); 3261590Srgrimes } 3271590Srgrimes | CDUP check_login CRLF 3281590Srgrimes = { 3291590Srgrimes if ($2) 3301590Srgrimes cwd(".."); 3311590Srgrimes } 3321590Srgrimes | SITE SP HELP CRLF 3331590Srgrimes = { 3341590Srgrimes help(sitetab, (char *) 0); 3351590Srgrimes } 3361590Srgrimes | SITE SP HELP SP STRING CRLF 3371590Srgrimes = { 3381590Srgrimes help(sitetab, (char *) $5); 3391590Srgrimes } 3401590Srgrimes | SITE SP UMASK check_login CRLF 3411590Srgrimes = { 3421590Srgrimes int oldmask; 3431590Srgrimes 3441590Srgrimes if ($4) { 3451590Srgrimes oldmask = umask(0); 3461590Srgrimes (void) umask(oldmask); 3471590Srgrimes reply(200, "Current UMASK is %03o", oldmask); 3481590Srgrimes } 3491590Srgrimes } 3501590Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 3511590Srgrimes = { 3521590Srgrimes int oldmask; 3531590Srgrimes 3541590Srgrimes if ($4) { 3551590Srgrimes if (($6 == -1) || ($6 > 0777)) { 3561590Srgrimes reply(501, "Bad UMASK value"); 3571590Srgrimes } else { 3581590Srgrimes oldmask = umask($6); 3591590Srgrimes reply(200, 3601590Srgrimes "UMASK set to %03o (was %03o)", 3611590Srgrimes $6, oldmask); 3621590Srgrimes } 3631590Srgrimes } 3641590Srgrimes } 3651590Srgrimes | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 3661590Srgrimes = { 3671590Srgrimes if ($4 && ($8 != NULL)) { 3681590Srgrimes if ($6 > 0777) 3691590Srgrimes reply(501, 3701590Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 3711590Srgrimes else if (chmod((char *) $8, $6) < 0) 3721590Srgrimes perror_reply(550, (char *) $8); 3731590Srgrimes else 3741590Srgrimes reply(200, "CHMOD command successful."); 3751590Srgrimes } 3761590Srgrimes if ($8 != NULL) 3771590Srgrimes free((char *) $8); 3781590Srgrimes } 3791590Srgrimes | SITE SP IDLE CRLF 3801590Srgrimes = { 3811590Srgrimes reply(200, 3821590Srgrimes "Current IDLE time limit is %d seconds; max %d", 3831590Srgrimes timeout, maxtimeout); 3841590Srgrimes } 3851590Srgrimes | SITE SP IDLE SP NUMBER CRLF 3861590Srgrimes = { 3871590Srgrimes if ($5 < 30 || $5 > maxtimeout) { 3881590Srgrimes reply(501, 3891590Srgrimes "Maximum IDLE time must be between 30 and %d seconds", 3901590Srgrimes maxtimeout); 3911590Srgrimes } else { 3921590Srgrimes timeout = $5; 3931590Srgrimes (void) alarm((unsigned) timeout); 3941590Srgrimes reply(200, 3951590Srgrimes "Maximum IDLE time set to %d seconds", 3961590Srgrimes timeout); 3971590Srgrimes } 3981590Srgrimes } 3991590Srgrimes | STOU check_login SP pathname CRLF 4001590Srgrimes = { 4011590Srgrimes if ($2 && $4 != NULL) 4021590Srgrimes store((char *) $4, "w", 1); 4031590Srgrimes if ($4 != NULL) 4041590Srgrimes free((char *) $4); 4051590Srgrimes } 4061590Srgrimes | SYST CRLF 4071590Srgrimes = { 4081590Srgrimes#ifdef unix 4091590Srgrimes#ifdef BSD 4101590Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 4111590Srgrimes NBBY, BSD); 4121590Srgrimes#else /* BSD */ 4131590Srgrimes reply(215, "UNIX Type: L%d", NBBY); 4141590Srgrimes#endif /* BSD */ 4151590Srgrimes#else /* unix */ 4161590Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 4171590Srgrimes#endif /* unix */ 4181590Srgrimes } 4191590Srgrimes 4201590Srgrimes /* 4211590Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 4221590Srgrimes * it will be in the updated RFC. 4231590Srgrimes * 4241590Srgrimes * Return size of file in a format suitable for 4251590Srgrimes * using with RESTART (we just count bytes). 4261590Srgrimes */ 4271590Srgrimes | SIZE check_login SP pathname CRLF 4281590Srgrimes = { 4291590Srgrimes if ($2 && $4 != NULL) 4301590Srgrimes sizecmd((char *) $4); 4311590Srgrimes if ($4 != NULL) 4321590Srgrimes free((char *) $4); 4331590Srgrimes } 4341590Srgrimes 4351590Srgrimes /* 4361590Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 4371590Srgrimes * it will be in the updated RFC. 4381590Srgrimes * 4391590Srgrimes * Return modification time of file as an ISO 3307 4401590Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 4411590Srgrimes * where xxx is the fractional second (of any precision, 4421590Srgrimes * not necessarily 3 digits) 4431590Srgrimes */ 4441590Srgrimes | MDTM check_login SP pathname CRLF 4451590Srgrimes = { 4461590Srgrimes if ($2 && $4 != NULL) { 4471590Srgrimes struct stat stbuf; 4481590Srgrimes if (stat((char *) $4, &stbuf) < 0) 4491590Srgrimes perror_reply(550, "%s", (char *) $4); 4501590Srgrimes else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 4511590Srgrimes reply(550, "%s: not a plain file.", 4521590Srgrimes (char *) $4); 4531590Srgrimes } else { 4541590Srgrimes register struct tm *t; 4551590Srgrimes struct tm *gmtime(); 4561590Srgrimes t = gmtime(&stbuf.st_mtime); 4571590Srgrimes reply(213, 45842816Sdanny "%d%02d%02d%02d%02d%02d", 45942816Sdanny t->tm_year+1900, t->tm_mon+1, t->tm_mday, 4601590Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 4611590Srgrimes } 4621590Srgrimes } 4631590Srgrimes if ($4 != NULL) 4641590Srgrimes free((char *) $4); 4651590Srgrimes } 4661590Srgrimes | QUIT CRLF 4671590Srgrimes = { 4681590Srgrimes reply(221, "Goodbye."); 4691590Srgrimes dologout(0); 4701590Srgrimes } 4711590Srgrimes | error CRLF 4721590Srgrimes = { 4731590Srgrimes yyerrok; 4741590Srgrimes } 4751590Srgrimes ; 4761590Srgrimesrcmd: RNFR check_login SP pathname CRLF 4771590Srgrimes = { 4781590Srgrimes char *renamefrom(); 4791590Srgrimes 4801590Srgrimes if ($2 && $4) { 4811590Srgrimes fromname = renamefrom((char *) $4); 4821590Srgrimes if (fromname == (char *) 0 && $4) { 4831590Srgrimes free((char *) $4); 4841590Srgrimes } 4851590Srgrimes } 4861590Srgrimes } 4871590Srgrimes ; 4881590Srgrimes 4891590Srgrimesusername: STRING 4901590Srgrimes ; 4911590Srgrimes 4921590Srgrimespassword: /* empty */ 4931590Srgrimes = { 4941590Srgrimes *(char **)&($$) = ""; 4951590Srgrimes } 4961590Srgrimes | STRING 4971590Srgrimes ; 4981590Srgrimes 4991590Srgrimesbyte_size: NUMBER 5001590Srgrimes ; 5011590Srgrimes 5021590Srgrimeshost_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 5031590Srgrimes NUMBER COMMA NUMBER 5041590Srgrimes = { 5051590Srgrimes register char *a, *p; 5061590Srgrimes 5071590Srgrimes a = (char *)&data_dest.sin_addr; 5081590Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 5091590Srgrimes p = (char *)&data_dest.sin_port; 5101590Srgrimes p[0] = $9; p[1] = $11; 5111590Srgrimes data_dest.sin_family = AF_INET; 5121590Srgrimes } 5131590Srgrimes ; 5141590Srgrimes 5151590Srgrimesform_code: N 5161590Srgrimes = { 5171590Srgrimes $$ = FORM_N; 5181590Srgrimes } 5191590Srgrimes | T 5201590Srgrimes = { 5211590Srgrimes $$ = FORM_T; 5221590Srgrimes } 5231590Srgrimes | C 5241590Srgrimes = { 5251590Srgrimes $$ = FORM_C; 5261590Srgrimes } 5271590Srgrimes ; 5281590Srgrimes 5291590Srgrimestype_code: A 5301590Srgrimes = { 5311590Srgrimes cmd_type = TYPE_A; 5321590Srgrimes cmd_form = FORM_N; 5331590Srgrimes } 5341590Srgrimes | A SP form_code 5351590Srgrimes = { 5361590Srgrimes cmd_type = TYPE_A; 5371590Srgrimes cmd_form = $3; 5381590Srgrimes } 5391590Srgrimes | E 5401590Srgrimes = { 5411590Srgrimes cmd_type = TYPE_E; 5421590Srgrimes cmd_form = FORM_N; 5431590Srgrimes } 5441590Srgrimes | E SP form_code 5451590Srgrimes = { 5461590Srgrimes cmd_type = TYPE_E; 5471590Srgrimes cmd_form = $3; 5481590Srgrimes } 5491590Srgrimes | I 5501590Srgrimes = { 5511590Srgrimes cmd_type = TYPE_I; 5521590Srgrimes } 5531590Srgrimes | L 5541590Srgrimes = { 5551590Srgrimes cmd_type = TYPE_L; 5561590Srgrimes cmd_bytesz = NBBY; 5571590Srgrimes } 5581590Srgrimes | L SP byte_size 5591590Srgrimes = { 5601590Srgrimes cmd_type = TYPE_L; 5611590Srgrimes cmd_bytesz = $3; 5621590Srgrimes } 5631590Srgrimes /* this is for a bug in the BBN ftp */ 5641590Srgrimes | L byte_size 5651590Srgrimes = { 5661590Srgrimes cmd_type = TYPE_L; 5671590Srgrimes cmd_bytesz = $2; 5681590Srgrimes } 5691590Srgrimes ; 5701590Srgrimes 5711590Srgrimesstruct_code: F 5721590Srgrimes = { 5731590Srgrimes $$ = STRU_F; 5741590Srgrimes } 5751590Srgrimes | R 5761590Srgrimes = { 5771590Srgrimes $$ = STRU_R; 5781590Srgrimes } 5791590Srgrimes | P 5801590Srgrimes = { 5811590Srgrimes $$ = STRU_P; 5821590Srgrimes } 5831590Srgrimes ; 5841590Srgrimes 5851590Srgrimesmode_code: S 5861590Srgrimes = { 5871590Srgrimes $$ = MODE_S; 5881590Srgrimes } 5891590Srgrimes | B 5901590Srgrimes = { 5911590Srgrimes $$ = MODE_B; 5921590Srgrimes } 5931590Srgrimes | C 5941590Srgrimes = { 5951590Srgrimes $$ = MODE_C; 5961590Srgrimes } 5971590Srgrimes ; 5981590Srgrimes 5991590Srgrimespathname: pathstring 6001590Srgrimes = { 6011590Srgrimes /* 6021590Srgrimes * Problem: this production is used for all pathname 6031590Srgrimes * processing, but only gives a 550 error reply. 6041590Srgrimes * This is a valid reply in some cases but not in others. 6051590Srgrimes */ 6061590Srgrimes if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 6071590Srgrimes *(char **)&($$) = *glob((char *) $1); 6081590Srgrimes if (globerr != NULL) { 6091590Srgrimes reply(550, globerr); 6101590Srgrimes $$ = NULL; 6111590Srgrimes } 6121590Srgrimes free((char *) $1); 6131590Srgrimes } else 6141590Srgrimes $$ = $1; 6151590Srgrimes } 6161590Srgrimes ; 6171590Srgrimes 6181590Srgrimespathstring: STRING 6191590Srgrimes ; 6201590Srgrimes 6211590Srgrimesoctal_number: NUMBER 6221590Srgrimes = { 6231590Srgrimes register int ret, dec, multby, digit; 6241590Srgrimes 6251590Srgrimes /* 6261590Srgrimes * Convert a number that was read as decimal number 6271590Srgrimes * to what it would be if it had been read as octal. 6281590Srgrimes */ 6291590Srgrimes dec = $1; 6301590Srgrimes multby = 1; 6311590Srgrimes ret = 0; 6321590Srgrimes while (dec) { 6331590Srgrimes digit = dec%10; 6341590Srgrimes if (digit > 7) { 6351590Srgrimes ret = -1; 6361590Srgrimes break; 6371590Srgrimes } 6381590Srgrimes ret += digit * multby; 6391590Srgrimes multby *= 8; 6401590Srgrimes dec /= 10; 6411590Srgrimes } 6421590Srgrimes $$ = ret; 6431590Srgrimes } 6441590Srgrimes ; 6451590Srgrimes 6461590Srgrimescheck_login: /* empty */ 6471590Srgrimes = { 6481590Srgrimes if (logged_in) 6491590Srgrimes $$ = 1; 6501590Srgrimes else { 6511590Srgrimes reply(530, "Please login with USER and PASS."); 6521590Srgrimes $$ = 0; 6531590Srgrimes } 6541590Srgrimes } 6551590Srgrimes ; 6561590Srgrimes 6571590Srgrimes%% 6581590Srgrimes 6591590Srgrimesextern jmp_buf errcatch; 6601590Srgrimes 6611590Srgrimes#define CMD 0 /* beginning of command */ 6621590Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 6631590Srgrimes#define STR1 2 /* expect SP followed by STRING */ 6641590Srgrimes#define STR2 3 /* expect STRING */ 6651590Srgrimes#define OSTR 4 /* optional SP then STRING */ 6661590Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 6671590Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 6681590Srgrimes#define SITECMD 7 /* SITE command */ 6691590Srgrimes#define NSTR 8 /* Number followed by a string */ 6701590Srgrimes 6711590Srgrimesstruct tab { 6721590Srgrimes char *name; 6731590Srgrimes short token; 6741590Srgrimes short state; 6751590Srgrimes short implemented; /* 1 if command is implemented */ 6761590Srgrimes char *help; 6771590Srgrimes}; 6781590Srgrimes 6791590Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 6801590Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 6811590Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 6821590Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 6831590Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 6841590Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 6851590Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 6861590Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 6871590Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 6881590Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 6891590Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 6901590Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 6911590Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 6921590Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 6931590Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 6941590Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 6951590Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 6961590Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 6971590Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 6981590Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 6991590Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 7001590Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 7011590Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 7021590Srgrimes { "REST", REST, ARGS, 0, "(restart command)" }, 7031590Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 7041590Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 7051590Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 7061590Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 7071590Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 7081590Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 7091590Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 7101590Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 7111590Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 7121590Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 7131590Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 7141590Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 7151590Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 7161590Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 7171590Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 7181590Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 7191590Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 7201590Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 7211590Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 7221590Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 7231590Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 7241590Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 7251590Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 7261590Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 7271590Srgrimes { NULL, 0, 0, 0, 0 } 7281590Srgrimes}; 7291590Srgrimes 7301590Srgrimesstruct tab sitetab[] = { 7311590Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 7321590Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 7331590Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 7341590Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 7351590Srgrimes { NULL, 0, 0, 0, 0 } 7361590Srgrimes}; 7371590Srgrimes 7381590Srgrimesstruct tab * 7391590Srgrimeslookup(p, cmd) 7401590Srgrimes register struct tab *p; 7411590Srgrimes char *cmd; 7421590Srgrimes{ 7431590Srgrimes 7441590Srgrimes for (; p->name != NULL; p++) 7451590Srgrimes if (strcmp(cmd, p->name) == 0) 7461590Srgrimes return (p); 7471590Srgrimes return (0); 7481590Srgrimes} 7491590Srgrimes 7501590Srgrimes#include <arpa/telnet.h> 7511590Srgrimes 7521590Srgrimes/* 7531590Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 7541590Srgrimes */ 7551590Srgrimeschar * 7561590Srgrimesgetline(s, n, iop) 7571590Srgrimes char *s; 7581590Srgrimes register FILE *iop; 7591590Srgrimes{ 7601590Srgrimes register c; 7611590Srgrimes register char *cs; 7621590Srgrimes 7631590Srgrimes cs = s; 7641590Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 7651590Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 7661590Srgrimes *cs++ = tmpline[c]; 7671590Srgrimes if (tmpline[c] == '\n') { 7681590Srgrimes *cs++ = '\0'; 7691590Srgrimes if (debug) 7701590Srgrimes syslog(LOG_DEBUG, "command: %s", s); 7711590Srgrimes tmpline[0] = '\0'; 7721590Srgrimes return(s); 7731590Srgrimes } 7741590Srgrimes if (c == 0) 7751590Srgrimes tmpline[0] = '\0'; 7761590Srgrimes } 7771590Srgrimes while ((c = getc(iop)) != EOF) { 7781590Srgrimes c &= 0377; 7791590Srgrimes if (c == IAC) { 7801590Srgrimes if ((c = getc(iop)) != EOF) { 7811590Srgrimes c &= 0377; 7821590Srgrimes switch (c) { 7831590Srgrimes case WILL: 7841590Srgrimes case WONT: 7851590Srgrimes c = getc(iop); 7861590Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 7871590Srgrimes (void) fflush(stdout); 7881590Srgrimes continue; 7891590Srgrimes case DO: 7901590Srgrimes case DONT: 7911590Srgrimes c = getc(iop); 7921590Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 7931590Srgrimes (void) fflush(stdout); 7941590Srgrimes continue; 7951590Srgrimes case IAC: 7961590Srgrimes break; 7971590Srgrimes default: 7981590Srgrimes continue; /* ignore command */ 7991590Srgrimes } 8001590Srgrimes } 8011590Srgrimes } 8021590Srgrimes *cs++ = c; 8031590Srgrimes if (--n <= 0 || c == '\n') 8041590Srgrimes break; 8051590Srgrimes } 8061590Srgrimes if (c == EOF && cs == s) 8071590Srgrimes return (NULL); 8081590Srgrimes *cs++ = '\0'; 8091590Srgrimes if (debug) 8101590Srgrimes syslog(LOG_DEBUG, "command: %s", s); 8111590Srgrimes return (s); 8121590Srgrimes} 8131590Srgrimes 8141590Srgrimesstatic int 8151590Srgrimestoolong() 8161590Srgrimes{ 8171590Srgrimes time_t now; 8181590Srgrimes extern char *ctime(); 8191590Srgrimes extern time_t time(); 8201590Srgrimes 8211590Srgrimes reply(421, 8221590Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 8231590Srgrimes (void) time(&now); 8241590Srgrimes if (logging) { 8251590Srgrimes syslog(LOG_INFO, 8261590Srgrimes "User %s timed out after %d seconds at %s", 8271590Srgrimes (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 8281590Srgrimes } 8291590Srgrimes dologout(1); 8301590Srgrimes} 8311590Srgrimes 8321590Srgrimesyylex() 8331590Srgrimes{ 8341590Srgrimes static int cpos, state; 8351590Srgrimes register char *cp, *cp2; 8361590Srgrimes register struct tab *p; 8371590Srgrimes int n; 8381590Srgrimes char c, *strpbrk(); 8391590Srgrimes char *copy(); 8401590Srgrimes 8411590Srgrimes for (;;) { 8421590Srgrimes switch (state) { 8431590Srgrimes 8441590Srgrimes case CMD: 8451590Srgrimes (void) signal(SIGALRM, toolong); 8461590Srgrimes (void) alarm((unsigned) timeout); 8471590Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 8481590Srgrimes reply(221, "You could at least say goodbye."); 8491590Srgrimes dologout(0); 8501590Srgrimes } 8511590Srgrimes (void) alarm(0); 8521590Srgrimes#ifdef SETPROCTITLE 8531590Srgrimes if (strncasecmp(cbuf, "PASS", 4) != NULL) 8541590Srgrimes setproctitle("%s: %s", proctitle, cbuf); 8551590Srgrimes#endif /* SETPROCTITLE */ 8561590Srgrimes if ((cp = index(cbuf, '\r'))) { 8571590Srgrimes *cp++ = '\n'; 8581590Srgrimes *cp = '\0'; 8591590Srgrimes } 8601590Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 8611590Srgrimes cpos = cp - cbuf; 8621590Srgrimes if (cpos == 0) 8631590Srgrimes cpos = 4; 8641590Srgrimes c = cbuf[cpos]; 8651590Srgrimes cbuf[cpos] = '\0'; 8661590Srgrimes upper(cbuf); 8671590Srgrimes p = lookup(cmdtab, cbuf); 8681590Srgrimes cbuf[cpos] = c; 8691590Srgrimes if (p != 0) { 8701590Srgrimes if (p->implemented == 0) { 8711590Srgrimes nack(p->name); 8721590Srgrimes longjmp(errcatch,0); 8731590Srgrimes /* NOTREACHED */ 8741590Srgrimes } 8751590Srgrimes state = p->state; 8761590Srgrimes *(char **)&yylval = p->name; 8771590Srgrimes return (p->token); 8781590Srgrimes } 8791590Srgrimes break; 8801590Srgrimes 8811590Srgrimes case SITECMD: 8821590Srgrimes if (cbuf[cpos] == ' ') { 8831590Srgrimes cpos++; 8841590Srgrimes return (SP); 8851590Srgrimes } 8861590Srgrimes cp = &cbuf[cpos]; 8871590Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 8881590Srgrimes cpos = cp2 - cbuf; 8891590Srgrimes c = cbuf[cpos]; 8901590Srgrimes cbuf[cpos] = '\0'; 8911590Srgrimes upper(cp); 8921590Srgrimes p = lookup(sitetab, cp); 8931590Srgrimes cbuf[cpos] = c; 8941590Srgrimes if (p != 0) { 8951590Srgrimes if (p->implemented == 0) { 8961590Srgrimes state = CMD; 8971590Srgrimes nack(p->name); 8981590Srgrimes longjmp(errcatch,0); 8991590Srgrimes /* NOTREACHED */ 9001590Srgrimes } 9011590Srgrimes state = p->state; 9021590Srgrimes *(char **)&yylval = p->name; 9031590Srgrimes return (p->token); 9041590Srgrimes } 9051590Srgrimes state = CMD; 9061590Srgrimes break; 9071590Srgrimes 9081590Srgrimes case OSTR: 9091590Srgrimes if (cbuf[cpos] == '\n') { 9101590Srgrimes state = CMD; 9111590Srgrimes return (CRLF); 9121590Srgrimes } 9131590Srgrimes /* FALLTHROUGH */ 9141590Srgrimes 9151590Srgrimes case STR1: 9161590Srgrimes case ZSTR1: 9171590Srgrimes dostr1: 9181590Srgrimes if (cbuf[cpos] == ' ') { 9191590Srgrimes cpos++; 9201590Srgrimes state = state == OSTR ? STR2 : ++state; 9211590Srgrimes return (SP); 9221590Srgrimes } 9231590Srgrimes break; 9241590Srgrimes 9251590Srgrimes case ZSTR2: 9261590Srgrimes if (cbuf[cpos] == '\n') { 9271590Srgrimes state = CMD; 9281590Srgrimes return (CRLF); 9291590Srgrimes } 9301590Srgrimes /* FALLTHROUGH */ 9311590Srgrimes 9321590Srgrimes case STR2: 9331590Srgrimes cp = &cbuf[cpos]; 9341590Srgrimes n = strlen(cp); 9351590Srgrimes cpos += n - 1; 9361590Srgrimes /* 9371590Srgrimes * Make sure the string is nonempty and \n terminated. 9381590Srgrimes */ 9391590Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 9401590Srgrimes cbuf[cpos] = '\0'; 9411590Srgrimes *(char **)&yylval = copy(cp); 9421590Srgrimes cbuf[cpos] = '\n'; 9431590Srgrimes state = ARGS; 9441590Srgrimes return (STRING); 9451590Srgrimes } 9461590Srgrimes break; 9471590Srgrimes 9481590Srgrimes case NSTR: 9491590Srgrimes if (cbuf[cpos] == ' ') { 9501590Srgrimes cpos++; 9511590Srgrimes return (SP); 9521590Srgrimes } 9531590Srgrimes if (isdigit(cbuf[cpos])) { 9541590Srgrimes cp = &cbuf[cpos]; 9551590Srgrimes while (isdigit(cbuf[++cpos])) 9561590Srgrimes ; 9571590Srgrimes c = cbuf[cpos]; 9581590Srgrimes cbuf[cpos] = '\0'; 9591590Srgrimes yylval = atoi(cp); 9601590Srgrimes cbuf[cpos] = c; 9611590Srgrimes state = STR1; 9621590Srgrimes return (NUMBER); 9631590Srgrimes } 9641590Srgrimes state = STR1; 9651590Srgrimes goto dostr1; 9661590Srgrimes 9671590Srgrimes case ARGS: 9681590Srgrimes if (isdigit(cbuf[cpos])) { 9691590Srgrimes cp = &cbuf[cpos]; 9701590Srgrimes while (isdigit(cbuf[++cpos])) 9711590Srgrimes ; 9721590Srgrimes c = cbuf[cpos]; 9731590Srgrimes cbuf[cpos] = '\0'; 9741590Srgrimes yylval = atoi(cp); 9751590Srgrimes cbuf[cpos] = c; 9761590Srgrimes return (NUMBER); 9771590Srgrimes } 9781590Srgrimes switch (cbuf[cpos++]) { 9791590Srgrimes 9801590Srgrimes case '\n': 9811590Srgrimes state = CMD; 9821590Srgrimes return (CRLF); 9831590Srgrimes 9841590Srgrimes case ' ': 9851590Srgrimes return (SP); 9861590Srgrimes 9871590Srgrimes case ',': 9881590Srgrimes return (COMMA); 9891590Srgrimes 9901590Srgrimes case 'A': 9911590Srgrimes case 'a': 9921590Srgrimes return (A); 9931590Srgrimes 9941590Srgrimes case 'B': 9951590Srgrimes case 'b': 9961590Srgrimes return (B); 9971590Srgrimes 9981590Srgrimes case 'C': 9991590Srgrimes case 'c': 10001590Srgrimes return (C); 10011590Srgrimes 10021590Srgrimes case 'E': 10031590Srgrimes case 'e': 10041590Srgrimes return (E); 10051590Srgrimes 10061590Srgrimes case 'F': 10071590Srgrimes case 'f': 10081590Srgrimes return (F); 10091590Srgrimes 10101590Srgrimes case 'I': 10111590Srgrimes case 'i': 10121590Srgrimes return (I); 10131590Srgrimes 10141590Srgrimes case 'L': 10151590Srgrimes case 'l': 10161590Srgrimes return (L); 10171590Srgrimes 10181590Srgrimes case 'N': 10191590Srgrimes case 'n': 10201590Srgrimes return (N); 10211590Srgrimes 10221590Srgrimes case 'P': 10231590Srgrimes case 'p': 10241590Srgrimes return (P); 10251590Srgrimes 10261590Srgrimes case 'R': 10271590Srgrimes case 'r': 10281590Srgrimes return (R); 10291590Srgrimes 10301590Srgrimes case 'S': 10311590Srgrimes case 's': 10321590Srgrimes return (S); 10331590Srgrimes 10341590Srgrimes case 'T': 10351590Srgrimes case 't': 10361590Srgrimes return (T); 10371590Srgrimes 10381590Srgrimes } 10391590Srgrimes break; 10401590Srgrimes 10411590Srgrimes default: 10421590Srgrimes fatal("Unknown state in scanner."); 10431590Srgrimes } 10441590Srgrimes yyerror((char *) 0); 10451590Srgrimes state = CMD; 10461590Srgrimes longjmp(errcatch,0); 10471590Srgrimes } 10481590Srgrimes} 10491590Srgrimes 10501590Srgrimesupper(s) 10511590Srgrimes register char *s; 10521590Srgrimes{ 10531590Srgrimes while (*s != '\0') { 10541590Srgrimes if (islower(*s)) 10551590Srgrimes *s = toupper(*s); 10561590Srgrimes s++; 10571590Srgrimes } 10581590Srgrimes} 10591590Srgrimes 10601590Srgrimeschar * 10611590Srgrimescopy(s) 10621590Srgrimes char *s; 10631590Srgrimes{ 10641590Srgrimes char *p; 10651590Srgrimes extern char *malloc(), *strcpy(); 10661590Srgrimes 10671590Srgrimes p = malloc((unsigned) strlen(s) + 1); 10681590Srgrimes if (p == NULL) 10691590Srgrimes fatal("Ran out of memory."); 10701590Srgrimes (void) strcpy(p, s); 10711590Srgrimes return (p); 10721590Srgrimes} 10731590Srgrimes 10741590Srgrimeshelp(ctab, s) 10751590Srgrimes struct tab *ctab; 10761590Srgrimes char *s; 10771590Srgrimes{ 10781590Srgrimes register struct tab *c; 10791590Srgrimes register int width, NCMDS; 10801590Srgrimes char *type; 10811590Srgrimes 10821590Srgrimes if (ctab == sitetab) 10831590Srgrimes type = "SITE "; 10841590Srgrimes else 10851590Srgrimes type = ""; 10861590Srgrimes width = 0, NCMDS = 0; 10871590Srgrimes for (c = ctab; c->name != NULL; c++) { 10881590Srgrimes int len = strlen(c->name); 10891590Srgrimes 10901590Srgrimes if (len > width) 10911590Srgrimes width = len; 10921590Srgrimes NCMDS++; 10931590Srgrimes } 10941590Srgrimes width = (width + 8) &~ 7; 10951590Srgrimes if (s == 0) { 10961590Srgrimes register int i, j, w; 10971590Srgrimes int columns, lines; 10981590Srgrimes 10991590Srgrimes lreply(214, "The following %scommands are recognized %s.", 11001590Srgrimes type, "(* =>'s unimplemented)"); 11011590Srgrimes columns = 76 / width; 11021590Srgrimes if (columns == 0) 11031590Srgrimes columns = 1; 11041590Srgrimes lines = (NCMDS + columns - 1) / columns; 11051590Srgrimes for (i = 0; i < lines; i++) { 11061590Srgrimes printf(" "); 11071590Srgrimes for (j = 0; j < columns; j++) { 11081590Srgrimes c = ctab + j * lines + i; 11091590Srgrimes printf("%s%c", c->name, 11101590Srgrimes c->implemented ? ' ' : '*'); 11111590Srgrimes if (c + lines >= &ctab[NCMDS]) 11121590Srgrimes break; 11131590Srgrimes w = strlen(c->name) + 1; 11141590Srgrimes while (w < width) { 11151590Srgrimes putchar(' '); 11161590Srgrimes w++; 11171590Srgrimes } 11181590Srgrimes } 11191590Srgrimes printf("\r\n"); 11201590Srgrimes } 11211590Srgrimes (void) fflush(stdout); 11221590Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 11231590Srgrimes return; 11241590Srgrimes } 11251590Srgrimes upper(s); 11261590Srgrimes c = lookup(ctab, s); 11271590Srgrimes if (c == (struct tab *)0) { 11281590Srgrimes reply(502, "Unknown command %s.", s); 11291590Srgrimes return; 11301590Srgrimes } 11311590Srgrimes if (c->implemented) 11321590Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 11331590Srgrimes else 11341590Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 11351590Srgrimes c->name, c->help); 11361590Srgrimes} 11371590Srgrimes 11381590Srgrimessizecmd(filename) 11391590Srgrimeschar *filename; 11401590Srgrimes{ 11411590Srgrimes switch (type) { 11421590Srgrimes case TYPE_L: 11431590Srgrimes case TYPE_I: { 11441590Srgrimes struct stat stbuf; 11451590Srgrimes if (stat(filename, &stbuf) < 0 || 11461590Srgrimes (stbuf.st_mode&S_IFMT) != S_IFREG) 11471590Srgrimes reply(550, "%s: not a plain file.", filename); 11481590Srgrimes else 11491590Srgrimes reply(213, "%lu", stbuf.st_size); 11501590Srgrimes break;} 11511590Srgrimes case TYPE_A: { 11521590Srgrimes FILE *fin; 11531590Srgrimes register int c, count; 11541590Srgrimes struct stat stbuf; 11551590Srgrimes fin = fopen(filename, "r"); 11561590Srgrimes if (fin == NULL) { 11571590Srgrimes perror_reply(550, filename); 11581590Srgrimes return; 11591590Srgrimes } 11601590Srgrimes if (fstat(fileno(fin), &stbuf) < 0 || 11611590Srgrimes (stbuf.st_mode&S_IFMT) != S_IFREG) { 11621590Srgrimes reply(550, "%s: not a plain file.", filename); 11631590Srgrimes (void) fclose(fin); 11641590Srgrimes return; 11651590Srgrimes } 11661590Srgrimes 11671590Srgrimes count = 0; 11681590Srgrimes while((c=getc(fin)) != EOF) { 11691590Srgrimes if (c == '\n') /* will get expanded to \r\n */ 11701590Srgrimes count++; 11711590Srgrimes count++; 11721590Srgrimes } 11731590Srgrimes (void) fclose(fin); 11741590Srgrimes 11751590Srgrimes reply(213, "%ld", count); 11761590Srgrimes break;} 11771590Srgrimes default: 11781590Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 11791590Srgrimes } 11801590Srgrimes} 1181