110276Ssam /* 2*66711Spendry * Copyright (c) 1985, 1988, 1993, 1994 361425Sbostic * The Regents of the University of California. All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 634769Sbostic * 7*66711Spendry * @(#)ftpcmd.y 8.2 (Berkeley) 04/04/94 822501Sdist */ 922501Sdist 1022501Sdist /* 1110276Ssam * Grammar for FTP commands. 1236933Skarels * See RFC 959. 1310276Ssam */ 1410276Ssam 1510276Ssam %{ 1610276Ssam 1710276Ssam #ifndef lint 18*66711Spendry static char sccsid[] = "@(#)ftpcmd.y 8.2 (Berkeley) 04/04/94"; 1933738Sbostic #endif /* not lint */ 2010276Ssam 2136552Sbostic #include <sys/param.h> 2210276Ssam #include <sys/socket.h> 2346669Sbostic #include <sys/stat.h> 2454528Sandrew 2510276Ssam #include <netinet/in.h> 2613033Ssam #include <arpa/ftp.h> 2754528Sandrew 28*66711Spendry #include <ctype.h> 29*66711Spendry #include <errno.h> 30*66711Spendry #include <glob.h> 31*66711Spendry #include <pwd.h> 32*66711Spendry #include <setjmp.h> 3311652Ssam #include <signal.h> 34*66711Spendry #include <stdio.h> 35*66711Spendry #include <stdlib.h> 36*66711Spendry #include <string.h> 3726494Sminshall #include <syslog.h> 3836933Skarels #include <time.h> 3946669Sbostic #include <unistd.h> 40*66711Spendry 4154528Sandrew #include "extern.h" 4210276Ssam 4310276Ssam extern struct sockaddr_in data_dest; 4410276Ssam extern int logged_in; 4510276Ssam extern struct passwd *pw; 4610276Ssam extern int guest; 4710276Ssam extern int logging; 4810276Ssam extern int type; 4910276Ssam extern int form; 5010276Ssam extern int debug; 5111652Ssam extern int timeout; 5236933Skarels extern int maxtimeout; 5326045Sminshall extern int pdata; 5436620Srick extern char hostname[], remotehost[]; 5536933Skarels extern char proctitle[]; 5610320Ssam extern int usedefault; 5726045Sminshall extern int transflag; 5826045Sminshall extern char tmpline[]; 5910276Ssam 6038135Srick off_t restart_point; 6138135Srick 6210276Ssam static int cmd_type; 6310276Ssam static int cmd_form; 6410276Ssam static int cmd_bytesz; 6536304Skarels char cbuf[512]; 6636304Skarels char *fromname; 6710276Ssam 6810276Ssam %} 6910276Ssam 70*66711Spendry %union { 71*66711Spendry int i; 72*66711Spendry char *s; 73*66711Spendry } 74*66711Spendry 7510276Ssam %token 7610276Ssam A B C E F I 7710276Ssam L N P R S T 7810276Ssam 79*66711Spendry SP CRLF COMMA 8010276Ssam 8110276Ssam USER PASS ACCT REIN QUIT PORT 8210276Ssam PASV TYPE STRU MODE RETR STOR 8310276Ssam APPE MLFL MAIL MSND MSOM MSAM 8410276Ssam MRSQ MRCP ALLO REST RNFR RNTO 8510276Ssam ABOR DELE CWD LIST NLST SITE 8636620Srick STAT HELP NOOP MKD RMD PWD 8736933Skarels CDUP STOU SMNT SYST SIZE MDTM 8810276Ssam 8936933Skarels UMASK IDLE CHMOD 9036933Skarels 9110276Ssam LEXERR 9210276Ssam 93*66711Spendry %token <s> STRING 94*66711Spendry %token <i> NUMBER 95*66711Spendry 96*66711Spendry %type <i> check_login octal_number byte_size 97*66711Spendry %type <i> struct_code mode_code type_code form_code 98*66711Spendry %type <s> pathstring pathname password username 99*66711Spendry 10010276Ssam %start cmd_list 10110276Ssam 10210276Ssam %% 10310276Ssam 104*66711Spendry cmd_list 105*66711Spendry : /* empty */ 106*66711Spendry | cmd_list cmd 107*66711Spendry { 10830945Scsvsj fromname = (char *) 0; 10938135Srick restart_point = (off_t) 0; 11030945Scsvsj } 111*66711Spendry | cmd_list rcmd 11210276Ssam ; 11310276Ssam 114*66711Spendry cmd 115*66711Spendry : USER SP username CRLF 116*66711Spendry { 117*66711Spendry user($3); 118*66711Spendry free($3); 11910276Ssam } 120*66711Spendry | PASS SP password CRLF 121*66711Spendry { 122*66711Spendry pass($3); 123*66711Spendry free($3); 12410276Ssam } 125*66711Spendry | PORT SP host_port CRLF 126*66711Spendry { 12710320Ssam usedefault = 0; 12836304Skarels if (pdata >= 0) { 12926045Sminshall (void) close(pdata); 13036304Skarels pdata = -1; 13126045Sminshall } 13227107Smckusick reply(200, "PORT command successful."); 13310276Ssam } 134*66711Spendry | PASV CRLF 135*66711Spendry { 13626045Sminshall passive(); 13726045Sminshall } 138*66711Spendry | TYPE SP type_code CRLF 139*66711Spendry { 14010276Ssam switch (cmd_type) { 14110276Ssam 14210276Ssam case TYPE_A: 14310276Ssam if (cmd_form == FORM_N) { 14410276Ssam reply(200, "Type set to A."); 14510276Ssam type = cmd_type; 14610276Ssam form = cmd_form; 14710276Ssam } else 14810276Ssam reply(504, "Form must be N."); 14910276Ssam break; 15010276Ssam 15110276Ssam case TYPE_E: 15210276Ssam reply(504, "Type E not implemented."); 15310276Ssam break; 15410276Ssam 15510276Ssam case TYPE_I: 15610276Ssam reply(200, "Type set to I."); 15710276Ssam type = cmd_type; 15810276Ssam break; 15910276Ssam 16010276Ssam case TYPE_L: 16136933Skarels #if NBBY == 8 16236933Skarels if (cmd_bytesz == 8) { 16310276Ssam reply(200, 16436933Skarels "Type set to L (byte size 8)."); 16510276Ssam type = cmd_type; 16610276Ssam } else 16736933Skarels reply(504, "Byte size must be 8."); 16836933Skarels #else /* NBBY == 8 */ 16936933Skarels UNIMPLEMENTED for NBBY != 8 17036933Skarels #endif /* NBBY == 8 */ 17110276Ssam } 17210276Ssam } 173*66711Spendry | STRU SP struct_code CRLF 174*66711Spendry { 17510276Ssam switch ($3) { 17610276Ssam 17710276Ssam case STRU_F: 17810276Ssam reply(200, "STRU F ok."); 17910276Ssam break; 18010276Ssam 18110276Ssam default: 18227107Smckusick reply(504, "Unimplemented STRU type."); 18310276Ssam } 18410276Ssam } 185*66711Spendry | MODE SP mode_code CRLF 186*66711Spendry { 18710276Ssam switch ($3) { 18810276Ssam 18910276Ssam case MODE_S: 19010276Ssam reply(200, "MODE S ok."); 19110276Ssam break; 19210276Ssam 19310276Ssam default: 19410276Ssam reply(502, "Unimplemented MODE type."); 19510276Ssam } 19610276Ssam } 197*66711Spendry | ALLO SP NUMBER CRLF 198*66711Spendry { 19927107Smckusick reply(202, "ALLO command ignored."); 20010276Ssam } 201*66711Spendry | ALLO SP NUMBER SP R SP NUMBER CRLF 202*66711Spendry { 20336933Skarels reply(202, "ALLO command ignored."); 20436933Skarels } 205*66711Spendry | RETR check_login SP pathname CRLF 206*66711Spendry { 20710302Ssam if ($2 && $4 != NULL) 208*66711Spendry retrieve((char *) 0, $4); 20910302Ssam if ($4 != NULL) 210*66711Spendry free($4); 21110276Ssam } 212*66711Spendry | STOR check_login SP pathname CRLF 213*66711Spendry { 21410302Ssam if ($2 && $4 != NULL) 215*66711Spendry store($4, "w", 0); 21610302Ssam if ($4 != NULL) 217*66711Spendry free($4); 21810276Ssam } 219*66711Spendry | APPE check_login SP pathname CRLF 220*66711Spendry { 22110302Ssam if ($2 && $4 != NULL) 222*66711Spendry store($4, "a", 0); 22310302Ssam if ($4 != NULL) 224*66711Spendry free($4); 22510276Ssam } 226*66711Spendry | NLST check_login CRLF 227*66711Spendry { 22810276Ssam if ($2) 22936620Srick send_file_list("."); 23010276Ssam } 231*66711Spendry | NLST check_login SP STRING CRLF 232*66711Spendry { 23352999Sbostic if ($2 && $4 != NULL) 234*66711Spendry send_file_list($4); 23510302Ssam if ($4 != NULL) 236*66711Spendry free($4); 23710276Ssam } 238*66711Spendry | LIST check_login CRLF 239*66711Spendry { 24010276Ssam if ($2) 24136620Srick retrieve("/bin/ls -lgA", ""); 24210276Ssam } 243*66711Spendry | LIST check_login SP pathname CRLF 244*66711Spendry { 24510302Ssam if ($2 && $4 != NULL) 246*66711Spendry retrieve("/bin/ls -lgA %s", $4); 24710302Ssam if ($4 != NULL) 248*66711Spendry free($4); 24910276Ssam } 250*66711Spendry | STAT check_login SP pathname CRLF 251*66711Spendry { 25236933Skarels if ($2 && $4 != NULL) 253*66711Spendry statfilecmd($4); 25436933Skarels if ($4 != NULL) 255*66711Spendry free($4); 25636933Skarels } 257*66711Spendry | STAT CRLF 258*66711Spendry { 25936933Skarels statcmd(); 26036933Skarels } 261*66711Spendry | DELE check_login SP pathname CRLF 262*66711Spendry { 26310302Ssam if ($2 && $4 != NULL) 264*66711Spendry delete($4); 26510302Ssam if ($4 != NULL) 266*66711Spendry free($4); 26710276Ssam } 268*66711Spendry | RNTO SP pathname CRLF 269*66711Spendry { 27030945Scsvsj if (fromname) { 271*66711Spendry renamecmd(fromname, $3); 27230945Scsvsj free(fromname); 27330945Scsvsj fromname = (char *) 0; 27430945Scsvsj } else { 27530945Scsvsj reply(503, "Bad sequence of commands."); 27630945Scsvsj } 277*66711Spendry free($3); 27830945Scsvsj } 279*66711Spendry | ABOR CRLF 280*66711Spendry { 28127107Smckusick reply(225, "ABOR command successful."); 28226045Sminshall } 283*66711Spendry | CWD check_login CRLF 284*66711Spendry { 28510276Ssam if ($2) 28610276Ssam cwd(pw->pw_dir); 28710276Ssam } 288*66711Spendry | CWD check_login SP pathname CRLF 289*66711Spendry { 29010302Ssam if ($2 && $4 != NULL) 291*66711Spendry cwd($4); 29210302Ssam if ($4 != NULL) 293*66711Spendry free($4); 29410276Ssam } 295*66711Spendry | HELP CRLF 296*66711Spendry { 29736933Skarels help(cmdtab, (char *) 0); 29810276Ssam } 299*66711Spendry | HELP SP STRING CRLF 300*66711Spendry { 301*66711Spendry char *cp = $3; 30236933Skarels 30336933Skarels if (strncasecmp(cp, "SITE", 4) == 0) { 304*66711Spendry cp = $3 + 4; 30536933Skarels if (*cp == ' ') 30636933Skarels cp++; 30736933Skarels if (*cp) 30836933Skarels help(sitetab, cp); 30936933Skarels else 31036933Skarels help(sitetab, (char *) 0); 31136933Skarels } else 312*66711Spendry help(cmdtab, $3); 31310276Ssam } 314*66711Spendry | NOOP CRLF 315*66711Spendry { 31627107Smckusick reply(200, "NOOP command successful."); 31710276Ssam } 318*66711Spendry | MKD check_login SP pathname CRLF 319*66711Spendry { 32010302Ssam if ($2 && $4 != NULL) 321*66711Spendry makedir($4); 32210302Ssam if ($4 != NULL) 323*66711Spendry free($4); 32410276Ssam } 325*66711Spendry | RMD check_login SP pathname CRLF 326*66711Spendry { 32710302Ssam if ($2 && $4 != NULL) 328*66711Spendry removedir($4); 32910302Ssam if ($4 != NULL) 330*66711Spendry free($4); 33110276Ssam } 332*66711Spendry | PWD check_login CRLF 333*66711Spendry { 33410276Ssam if ($2) 33510302Ssam pwd(); 33610276Ssam } 337*66711Spendry | CDUP check_login CRLF 338*66711Spendry { 33910276Ssam if ($2) 34010276Ssam cwd(".."); 34110276Ssam } 342*66711Spendry | SITE SP HELP CRLF 343*66711Spendry { 34436933Skarels help(sitetab, (char *) 0); 34536933Skarels } 346*66711Spendry | SITE SP HELP SP STRING CRLF 347*66711Spendry { 348*66711Spendry help(sitetab, $5); 34936933Skarels } 350*66711Spendry | SITE SP UMASK check_login CRLF 351*66711Spendry { 35236933Skarels int oldmask; 35336933Skarels 35436933Skarels if ($4) { 35536933Skarels oldmask = umask(0); 35636933Skarels (void) umask(oldmask); 35736933Skarels reply(200, "Current UMASK is %03o", oldmask); 35836933Skarels } 35936933Skarels } 360*66711Spendry | SITE SP UMASK check_login SP octal_number CRLF 361*66711Spendry { 36236933Skarels int oldmask; 36336933Skarels 36436933Skarels if ($4) { 36536933Skarels if (($6 == -1) || ($6 > 0777)) { 36636933Skarels reply(501, "Bad UMASK value"); 36736933Skarels } else { 36836933Skarels oldmask = umask($6); 36936933Skarels reply(200, 37036933Skarels "UMASK set to %03o (was %03o)", 37136933Skarels $6, oldmask); 37236933Skarels } 37336933Skarels } 37436933Skarels } 375*66711Spendry | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 376*66711Spendry { 37736933Skarels if ($4 && ($8 != NULL)) { 37836933Skarels if ($6 > 0777) 37936933Skarels reply(501, 38036933Skarels "CHMOD: Mode value must be between 0 and 0777"); 381*66711Spendry else if (chmod($8, $6) < 0) 382*66711Spendry perror_reply(550, $8); 38336933Skarels else 38436933Skarels reply(200, "CHMOD command successful."); 38536933Skarels } 38636933Skarels if ($8 != NULL) 387*66711Spendry free($8); 38836933Skarels } 389*66711Spendry | SITE SP IDLE CRLF 390*66711Spendry { 39136933Skarels reply(200, 39236933Skarels "Current IDLE time limit is %d seconds; max %d", 39336933Skarels timeout, maxtimeout); 39436933Skarels } 395*66711Spendry | SITE SP IDLE SP NUMBER CRLF 396*66711Spendry { 39736933Skarels if ($5 < 30 || $5 > maxtimeout) { 39836933Skarels reply(501, 39936933Skarels "Maximum IDLE time must be between 30 and %d seconds", 40036933Skarels maxtimeout); 40136933Skarels } else { 40236933Skarels timeout = $5; 40336933Skarels (void) alarm((unsigned) timeout); 40436933Skarels reply(200, 40536933Skarels "Maximum IDLE time set to %d seconds", 40636933Skarels timeout); 40736933Skarels } 40836933Skarels } 409*66711Spendry | STOU check_login SP pathname CRLF 410*66711Spendry { 41136304Skarels if ($2 && $4 != NULL) 412*66711Spendry store($4, "w", 1); 41326045Sminshall if ($4 != NULL) 414*66711Spendry free($4); 41526045Sminshall } 416*66711Spendry | SYST CRLF 417*66711Spendry { 41836640Srick #ifdef unix 41936933Skarels #ifdef BSD 42036552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 42136552Sbostic NBBY, BSD); 42236933Skarels #else /* BSD */ 42336933Skarels reply(215, "UNIX Type: L%d", NBBY); 42436933Skarels #endif /* BSD */ 42536933Skarels #else /* unix */ 42636933Skarels reply(215, "UNKNOWN Type: L%d", NBBY); 42736933Skarels #endif /* unix */ 42836552Sbostic } 42936933Skarels 43036933Skarels /* 43136933Skarels * SIZE is not in RFC959, but Postel has blessed it and 43236933Skarels * it will be in the updated RFC. 43336933Skarels * 43436933Skarels * Return size of file in a format suitable for 43536933Skarels * using with RESTART (we just count bytes). 43636933Skarels */ 437*66711Spendry | SIZE check_login SP pathname CRLF 438*66711Spendry { 43936933Skarels if ($2 && $4 != NULL) 440*66711Spendry sizecmd($4); 44136933Skarels if ($4 != NULL) 442*66711Spendry free($4); 44336933Skarels } 44436933Skarels 44536933Skarels /* 44636933Skarels * MDTM is not in RFC959, but Postel has blessed it and 44736933Skarels * it will be in the updated RFC. 44836933Skarels * 44936933Skarels * Return modification time of file as an ISO 3307 45036933Skarels * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 45136933Skarels * where xxx is the fractional second (of any precision, 45236933Skarels * not necessarily 3 digits) 45336933Skarels */ 454*66711Spendry | MDTM check_login SP pathname CRLF 455*66711Spendry { 45636933Skarels if ($2 && $4 != NULL) { 45736933Skarels struct stat stbuf; 458*66711Spendry if (stat($4, &stbuf) < 0) 45954528Sandrew reply(550, "%s: %s", 460*66711Spendry $4, strerror(errno)); 461*66711Spendry else if (!S_ISREG(stbuf.st_mode)) { 462*66711Spendry reply(550, "%s: not a plain file.", $4); 46336933Skarels } else { 464*66711Spendry struct tm *t; 46536933Skarels t = gmtime(&stbuf.st_mtime); 46636933Skarels reply(213, 46736933Skarels "19%02d%02d%02d%02d%02d%02d", 46836933Skarels t->tm_year, t->tm_mon+1, t->tm_mday, 46936933Skarels t->tm_hour, t->tm_min, t->tm_sec); 47036933Skarels } 47136933Skarels } 47236933Skarels if ($4 != NULL) 473*66711Spendry free($4); 47436933Skarels } 475*66711Spendry | QUIT CRLF 476*66711Spendry { 47710276Ssam reply(221, "Goodbye."); 47813246Ssam dologout(0); 47910276Ssam } 480*66711Spendry | error CRLF 481*66711Spendry { 48210276Ssam yyerrok; 48310276Ssam } 48410276Ssam ; 485*66711Spendry rcmd 486*66711Spendry : RNFR check_login SP pathname CRLF 487*66711Spendry { 48830945Scsvsj char *renamefrom(); 48930945Scsvsj 49038135Srick restart_point = (off_t) 0; 49130945Scsvsj if ($2 && $4) { 492*66711Spendry fromname = renamefrom($4); 49330945Scsvsj if (fromname == (char *) 0 && $4) { 494*66711Spendry free($4); 49530945Scsvsj } 49630945Scsvsj } 49730945Scsvsj } 498*66711Spendry | REST SP byte_size CRLF 499*66711Spendry { 50038135Srick fromname = (char *) 0; 50138135Srick restart_point = $3; 50238135Srick reply(350, "Restarting at %ld. %s", restart_point, 50338135Srick "Send STORE or RETRIEVE to initiate transfer."); 50438135Srick } 50530945Scsvsj ; 50652999Sbostic 507*66711Spendry username 508*66711Spendry : STRING 50910276Ssam ; 51010276Ssam 511*66711Spendry password 512*66711Spendry : /* empty */ 513*66711Spendry { 514*66711Spendry $$ = (char *)calloc(1, sizeof(char)); 51536304Skarels } 516*66711Spendry | STRING 51710276Ssam ; 51810276Ssam 519*66711Spendry byte_size 520*66711Spendry : NUMBER 52110276Ssam ; 52210276Ssam 523*66711Spendry host_port 524*66711Spendry : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 52510276Ssam NUMBER COMMA NUMBER 526*66711Spendry { 527*66711Spendry char *a, *p; 52810276Ssam 52910276Ssam a = (char *)&data_dest.sin_addr; 53010276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 53110276Ssam p = (char *)&data_dest.sin_port; 53210276Ssam p[0] = $9; p[1] = $11; 53310324Ssam data_dest.sin_family = AF_INET; 53410276Ssam } 53510276Ssam ; 53610276Ssam 537*66711Spendry form_code 538*66711Spendry : N 539*66711Spendry { 540*66711Spendry $$ = FORM_N; 541*66711Spendry } 542*66711Spendry | T 543*66711Spendry { 544*66711Spendry $$ = FORM_T; 545*66711Spendry } 546*66711Spendry | C 547*66711Spendry { 548*66711Spendry $$ = FORM_C; 549*66711Spendry } 55010276Ssam ; 55110276Ssam 552*66711Spendry type_code 553*66711Spendry : A 554*66711Spendry { 555*66711Spendry cmd_type = TYPE_A; 556*66711Spendry cmd_form = FORM_N; 557*66711Spendry } 558*66711Spendry | A SP form_code 559*66711Spendry { 560*66711Spendry cmd_type = TYPE_A; 561*66711Spendry cmd_form = $3; 562*66711Spendry } 563*66711Spendry | E 564*66711Spendry { 565*66711Spendry cmd_type = TYPE_E; 566*66711Spendry cmd_form = FORM_N; 567*66711Spendry } 568*66711Spendry | E SP form_code 569*66711Spendry { 570*66711Spendry cmd_type = TYPE_E; 571*66711Spendry cmd_form = $3; 572*66711Spendry } 573*66711Spendry | I 574*66711Spendry { 575*66711Spendry cmd_type = TYPE_I; 576*66711Spendry } 577*66711Spendry | L 578*66711Spendry { 579*66711Spendry cmd_type = TYPE_L; 580*66711Spendry cmd_bytesz = NBBY; 581*66711Spendry } 582*66711Spendry | L SP byte_size 583*66711Spendry { 584*66711Spendry cmd_type = TYPE_L; 585*66711Spendry cmd_bytesz = $3; 586*66711Spendry } 587*66711Spendry /* this is for a bug in the BBN ftp */ 588*66711Spendry | L byte_size 589*66711Spendry { 590*66711Spendry cmd_type = TYPE_L; 591*66711Spendry cmd_bytesz = $2; 592*66711Spendry } 59310276Ssam ; 59410276Ssam 595*66711Spendry struct_code 596*66711Spendry : F 597*66711Spendry { 598*66711Spendry $$ = STRU_F; 599*66711Spendry } 600*66711Spendry | R 601*66711Spendry { 602*66711Spendry $$ = STRU_R; 603*66711Spendry } 604*66711Spendry | P 605*66711Spendry { 606*66711Spendry $$ = STRU_P; 607*66711Spendry } 60810276Ssam ; 60910276Ssam 610*66711Spendry mode_code 611*66711Spendry : S 612*66711Spendry { 613*66711Spendry $$ = MODE_S; 614*66711Spendry } 615*66711Spendry | B 616*66711Spendry { 617*66711Spendry $$ = MODE_B; 618*66711Spendry } 619*66711Spendry | C 620*66711Spendry { 621*66711Spendry $$ = MODE_C; 622*66711Spendry } 62310276Ssam ; 62410276Ssam 625*66711Spendry pathname 626*66711Spendry : pathstring 627*66711Spendry { 628*66711Spendry /* 629*66711Spendry * Problem: this production is used for all pathname 630*66711Spendry * processing, but only gives a 550 error reply. 631*66711Spendry * This is a valid reply in some cases but not in others. 632*66711Spendry */ 633*66711Spendry if (logged_in && $1 && *$1 == '~') { 634*66711Spendry glob_t gl; 635*66711Spendry int flags = GLOB_BRACE|GLOB_QUOTE|GLOB_TILDE; 636*66711Spendry 637*66711Spendry memset(&gl, 0, sizeof(gl)); 638*66711Spendry if (glob($1, flags, NULL, &gl)) { 639*66711Spendry reply(550, "not found"); 640*66711Spendry $$ = NULL; 641*66711Spendry } else { 642*66711Spendry $$ = strdup(gl.gl_pathv[0]); 643*66711Spendry } 644*66711Spendry globfree(&gl); 645*66711Spendry free($1); 646*66711Spendry } else 647*66711Spendry $$ = $1; 648*66711Spendry } 64910276Ssam ; 65010276Ssam 651*66711Spendry pathstring 652*66711Spendry : STRING 65310276Ssam ; 65410276Ssam 655*66711Spendry octal_number 656*66711Spendry : NUMBER 657*66711Spendry { 658*66711Spendry int ret, dec, multby, digit; 65936933Skarels 660*66711Spendry /* 661*66711Spendry * Convert a number that was read as decimal number 662*66711Spendry * to what it would be if it had been read as octal. 663*66711Spendry */ 664*66711Spendry dec = $1; 665*66711Spendry multby = 1; 666*66711Spendry ret = 0; 667*66711Spendry while (dec) { 668*66711Spendry digit = dec%10; 669*66711Spendry if (digit > 7) { 670*66711Spendry ret = -1; 671*66711Spendry break; 672*66711Spendry } 673*66711Spendry ret += digit * multby; 674*66711Spendry multby *= 8; 675*66711Spendry dec /= 10; 67636933Skarels } 677*66711Spendry $$ = ret; 67836933Skarels } 67936933Skarels ; 68036933Skarels 681*66711Spendry 682*66711Spendry check_login 683*66711Spendry : /* empty */ 684*66711Spendry { 685*66711Spendry if (logged_in) 686*66711Spendry $$ = 1; 687*66711Spendry else { 688*66711Spendry reply(530, "Please login with USER and PASS."); 689*66711Spendry $$ = 0; 690*66711Spendry } 69110276Ssam } 69210276Ssam ; 69310276Ssam 69410276Ssam %% 69510276Ssam 69610276Ssam extern jmp_buf errcatch; 69710276Ssam 69810276Ssam #define CMD 0 /* beginning of command */ 69910276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 70010276Ssam #define STR1 2 /* expect SP followed by STRING */ 70110276Ssam #define STR2 3 /* expect STRING */ 70236304Skarels #define OSTR 4 /* optional SP then STRING */ 70336304Skarels #define ZSTR1 5 /* SP then optional STRING */ 70436304Skarels #define ZSTR2 6 /* optional STRING after SP */ 70536933Skarels #define SITECMD 7 /* SITE command */ 70636933Skarels #define NSTR 8 /* Number followed by a string */ 70710276Ssam 70810276Ssam struct tab { 70910276Ssam char *name; 71010276Ssam short token; 71110276Ssam short state; 71210276Ssam short implemented; /* 1 if command is implemented */ 71310276Ssam char *help; 71410276Ssam }; 71510276Ssam 71610276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 71710276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 71836304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 71910276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 72036933Skarels { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 72110276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 72210276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 72310276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 72426045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 72510276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 72610276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 72710276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 72810276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 72910276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 73010276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 73110276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 73210276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 73310276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 73410276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 73510276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 73610276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 73710276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 73810276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 73954058Sandrew { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 74010276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 74110276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 74226045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 74310276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 74436304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 74510276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 74610276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 74710276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 74836933Skarels { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 74936552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 75036933Skarels { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 75110276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 75210276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 75336620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 75436620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 75536620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 75636620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 75736620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 75836620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 75936620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 76036620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 76126045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 76236933Skarels { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 76336933Skarels { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 76410276Ssam { NULL, 0, 0, 0, 0 } 76510276Ssam }; 76610276Ssam 76736933Skarels struct tab sitetab[] = { 76836933Skarels { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 76936933Skarels { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 77036933Skarels { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 77136933Skarels { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 77236933Skarels { NULL, 0, 0, 0, 0 } 77336933Skarels }; 77436933Skarels 77554528Sandrew static char *copy __P((char *)); 77654528Sandrew static void help __P((struct tab *, char *)); 77754528Sandrew static struct tab * 77854528Sandrew lookup __P((struct tab *, char *)); 77954528Sandrew static void sizecmd __P((char *)); 78054528Sandrew static void toolong __P((int)); 78154528Sandrew static int yylex __P((void)); 78254528Sandrew 78354528Sandrew static struct tab * 78436933Skarels lookup(p, cmd) 785*66711Spendry struct tab *p; 78610276Ssam char *cmd; 78710276Ssam { 78810276Ssam 78936933Skarels for (; p->name != NULL; p++) 79010276Ssam if (strcmp(cmd, p->name) == 0) 79110276Ssam return (p); 79210276Ssam return (0); 79310276Ssam } 79410276Ssam 79513033Ssam #include <arpa/telnet.h> 79610276Ssam 79710276Ssam /* 79810276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 79910276Ssam */ 80010276Ssam char * 80110276Ssam getline(s, n, iop) 80210276Ssam char *s; 80354528Sandrew int n; 804*66711Spendry FILE *iop; 80510276Ssam { 806*66711Spendry int c; 80726494Sminshall register char *cs; 80810276Ssam 80910276Ssam cs = s; 81027751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 81126045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 81226045Sminshall *cs++ = tmpline[c]; 81326045Sminshall if (tmpline[c] == '\n') { 81426045Sminshall *cs++ = '\0'; 81536304Skarels if (debug) 81636304Skarels syslog(LOG_DEBUG, "command: %s", s); 81726045Sminshall tmpline[0] = '\0'; 81826045Sminshall return(s); 81926045Sminshall } 82036304Skarels if (c == 0) 82126045Sminshall tmpline[0] = '\0'; 82226045Sminshall } 82336304Skarels while ((c = getc(iop)) != EOF) { 82436304Skarels c &= 0377; 82536304Skarels if (c == IAC) { 82636304Skarels if ((c = getc(iop)) != EOF) { 82736304Skarels c &= 0377; 82836304Skarels switch (c) { 82927751Sminshall case WILL: 83027751Sminshall case WONT: 83136277Sbostic c = getc(iop); 83236304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 83327751Sminshall (void) fflush(stdout); 83436304Skarels continue; 83527751Sminshall case DO: 83627751Sminshall case DONT: 83736277Sbostic c = getc(iop); 83836304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 83927751Sminshall (void) fflush(stdout); 84036304Skarels continue; 84136304Skarels case IAC: 84227751Sminshall break; 84327751Sminshall default: 84436304Skarels continue; /* ignore command */ 84527751Sminshall } 84636304Skarels } 84710276Ssam } 84836304Skarels *cs++ = c; 84936304Skarels if (--n <= 0 || c == '\n') 85010276Ssam break; 85110276Ssam } 85227751Sminshall if (c == EOF && cs == s) 85318303Sralph return (NULL); 85410276Ssam *cs++ = '\0'; 85552999Sbostic if (debug) { 85652999Sbostic if (!guest && strncasecmp("pass ", s, 5) == 0) { 85752999Sbostic /* Don't syslog passwords */ 85852999Sbostic syslog(LOG_DEBUG, "command: %.5s ???", s); 85952999Sbostic } else { 86052999Sbostic register char *cp; 86152999Sbostic register int len; 86252999Sbostic 86352999Sbostic /* Don't syslog trailing CR-LF */ 86452999Sbostic len = strlen(s); 86552999Sbostic cp = s + len - 1; 86652999Sbostic while (cp >= s && (*cp == '\n' || *cp == '\r')) { 86752999Sbostic --cp; 86852999Sbostic --len; 86952999Sbostic } 87052999Sbostic syslog(LOG_DEBUG, "command: %.*s", len, s); 87152999Sbostic } 87252999Sbostic } 87310276Ssam return (s); 87410276Ssam } 87510276Ssam 87646669Sbostic static void 87754528Sandrew toolong(signo) 87854528Sandrew int signo; 87911652Ssam { 88011652Ssam 88111652Ssam reply(421, 88252999Sbostic "Timeout (%d seconds): closing control connection.", timeout); 88352999Sbostic if (logging) 88452999Sbostic syslog(LOG_INFO, "User %s timed out after %d seconds", 88552999Sbostic (pw ? pw -> pw_name : "unknown"), timeout); 88613246Ssam dologout(1); 88711652Ssam } 88811652Ssam 88954528Sandrew static int 89010276Ssam yylex() 89110276Ssam { 89210276Ssam static int cpos, state; 893*66711Spendry char *cp, *cp2; 894*66711Spendry struct tab *p; 89510276Ssam int n; 89654528Sandrew char c; 89710276Ssam 89810276Ssam for (;;) { 89910276Ssam switch (state) { 90010276Ssam 90110276Ssam case CMD: 90226494Sminshall (void) signal(SIGALRM, toolong); 90326494Sminshall (void) alarm((unsigned) timeout); 90410276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 90510276Ssam reply(221, "You could at least say goodbye."); 90613246Ssam dologout(0); 90710276Ssam } 90826494Sminshall (void) alarm(0); 90936620Srick #ifdef SETPROCTITLE 91036933Skarels if (strncasecmp(cbuf, "PASS", 4) != NULL) 91136933Skarels setproctitle("%s: %s", proctitle, cbuf); 91236620Srick #endif /* SETPROCTITLE */ 91360087Sbostic if ((cp = strchr(cbuf, '\r'))) { 91436324Sbostic *cp++ = '\n'; 91536324Sbostic *cp = '\0'; 91636324Sbostic } 91736277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 91836277Sbostic cpos = cp - cbuf; 91936304Skarels if (cpos == 0) 92010276Ssam cpos = 4; 92110276Ssam c = cbuf[cpos]; 92210276Ssam cbuf[cpos] = '\0'; 92310276Ssam upper(cbuf); 92436933Skarels p = lookup(cmdtab, cbuf); 92510276Ssam cbuf[cpos] = c; 92610276Ssam if (p != 0) { 92710276Ssam if (p->implemented == 0) { 92810276Ssam nack(p->name); 92926494Sminshall longjmp(errcatch,0); 93010276Ssam /* NOTREACHED */ 93110276Ssam } 93210276Ssam state = p->state; 933*66711Spendry yylval.s = p->name; 93410276Ssam return (p->token); 93510276Ssam } 93610276Ssam break; 93710276Ssam 93836933Skarels case SITECMD: 93936933Skarels if (cbuf[cpos] == ' ') { 94036933Skarels cpos++; 94136933Skarels return (SP); 94236933Skarels } 94336933Skarels cp = &cbuf[cpos]; 94436933Skarels if ((cp2 = strpbrk(cp, " \n"))) 94536933Skarels cpos = cp2 - cbuf; 94636933Skarels c = cbuf[cpos]; 94736933Skarels cbuf[cpos] = '\0'; 94836933Skarels upper(cp); 94936933Skarels p = lookup(sitetab, cp); 95036933Skarels cbuf[cpos] = c; 95136933Skarels if (p != 0) { 95236933Skarels if (p->implemented == 0) { 95336933Skarels state = CMD; 95436933Skarels nack(p->name); 95536933Skarels longjmp(errcatch,0); 95636933Skarels /* NOTREACHED */ 95736933Skarels } 95836933Skarels state = p->state; 959*66711Spendry yylval.s = p->name; 96036933Skarels return (p->token); 96136933Skarels } 96236933Skarels state = CMD; 96336933Skarels break; 96436933Skarels 96510276Ssam case OSTR: 96610276Ssam if (cbuf[cpos] == '\n') { 96710276Ssam state = CMD; 96810276Ssam return (CRLF); 96910276Ssam } 97036317Sbostic /* FALLTHROUGH */ 97110276Ssam 97210276Ssam case STR1: 97336304Skarels case ZSTR1: 97436933Skarels dostr1: 97510276Ssam if (cbuf[cpos] == ' ') { 97610276Ssam cpos++; 97736317Sbostic state = state == OSTR ? STR2 : ++state; 97810276Ssam return (SP); 97910276Ssam } 98010276Ssam break; 98110276Ssam 98236304Skarels case ZSTR2: 98336304Skarels if (cbuf[cpos] == '\n') { 98436304Skarels state = CMD; 98536304Skarels return (CRLF); 98636304Skarels } 98736933Skarels /* FALLTHROUGH */ 98836304Skarels 98910276Ssam case STR2: 99010276Ssam cp = &cbuf[cpos]; 99110276Ssam n = strlen(cp); 99210276Ssam cpos += n - 1; 99310276Ssam /* 99410276Ssam * Make sure the string is nonempty and \n terminated. 99510276Ssam */ 99610276Ssam if (n > 1 && cbuf[cpos] == '\n') { 99710276Ssam cbuf[cpos] = '\0'; 998*66711Spendry yylval.s = copy(cp); 99910276Ssam cbuf[cpos] = '\n'; 100010276Ssam state = ARGS; 100110276Ssam return (STRING); 100210276Ssam } 100310276Ssam break; 100410276Ssam 100536933Skarels case NSTR: 100636933Skarels if (cbuf[cpos] == ' ') { 100736933Skarels cpos++; 100836933Skarels return (SP); 100936933Skarels } 101036933Skarels if (isdigit(cbuf[cpos])) { 101136933Skarels cp = &cbuf[cpos]; 101236933Skarels while (isdigit(cbuf[++cpos])) 101336933Skarels ; 101436933Skarels c = cbuf[cpos]; 101536933Skarels cbuf[cpos] = '\0'; 1016*66711Spendry yylval.i = atoi(cp); 101736933Skarels cbuf[cpos] = c; 101836933Skarels state = STR1; 101936933Skarels return (NUMBER); 102036933Skarels } 102136933Skarels state = STR1; 102236933Skarels goto dostr1; 102336933Skarels 102410276Ssam case ARGS: 102510276Ssam if (isdigit(cbuf[cpos])) { 102610276Ssam cp = &cbuf[cpos]; 102710276Ssam while (isdigit(cbuf[++cpos])) 102810276Ssam ; 102910276Ssam c = cbuf[cpos]; 103010276Ssam cbuf[cpos] = '\0'; 1031*66711Spendry yylval.i = atoi(cp); 103210276Ssam cbuf[cpos] = c; 103310276Ssam return (NUMBER); 103410276Ssam } 103510276Ssam switch (cbuf[cpos++]) { 103610276Ssam 103710276Ssam case '\n': 103810276Ssam state = CMD; 103910276Ssam return (CRLF); 104010276Ssam 104110276Ssam case ' ': 104210276Ssam return (SP); 104310276Ssam 104410276Ssam case ',': 104510276Ssam return (COMMA); 104610276Ssam 104710276Ssam case 'A': 104810276Ssam case 'a': 104910276Ssam return (A); 105010276Ssam 105110276Ssam case 'B': 105210276Ssam case 'b': 105310276Ssam return (B); 105410276Ssam 105510276Ssam case 'C': 105610276Ssam case 'c': 105710276Ssam return (C); 105810276Ssam 105910276Ssam case 'E': 106010276Ssam case 'e': 106110276Ssam return (E); 106210276Ssam 106310276Ssam case 'F': 106410276Ssam case 'f': 106510276Ssam return (F); 106610276Ssam 106710276Ssam case 'I': 106810276Ssam case 'i': 106910276Ssam return (I); 107010276Ssam 107110276Ssam case 'L': 107210276Ssam case 'l': 107310276Ssam return (L); 107410276Ssam 107510276Ssam case 'N': 107610276Ssam case 'n': 107710276Ssam return (N); 107810276Ssam 107910276Ssam case 'P': 108010276Ssam case 'p': 108110276Ssam return (P); 108210276Ssam 108310276Ssam case 'R': 108410276Ssam case 'r': 108510276Ssam return (R); 108610276Ssam 108710276Ssam case 'S': 108810276Ssam case 's': 108910276Ssam return (S); 109010276Ssam 109110276Ssam case 'T': 109210276Ssam case 't': 109310276Ssam return (T); 109410276Ssam 109510276Ssam } 109610276Ssam break; 109710276Ssam 109810276Ssam default: 109910276Ssam fatal("Unknown state in scanner."); 110010276Ssam } 110126494Sminshall yyerror((char *) 0); 110210276Ssam state = CMD; 110326494Sminshall longjmp(errcatch,0); 110410276Ssam } 110510276Ssam } 110610276Ssam 110754528Sandrew void 110810276Ssam upper(s) 1109*66711Spendry char *s; 111010276Ssam { 111110276Ssam while (*s != '\0') { 111210276Ssam if (islower(*s)) 111310276Ssam *s = toupper(*s); 111410276Ssam s++; 111510276Ssam } 111610276Ssam } 111710276Ssam 111854528Sandrew static char * 111910276Ssam copy(s) 112010276Ssam char *s; 112110276Ssam { 112210276Ssam char *p; 112310276Ssam 112426494Sminshall p = malloc((unsigned) strlen(s) + 1); 112510276Ssam if (p == NULL) 112610276Ssam fatal("Ran out of memory."); 112726494Sminshall (void) strcpy(p, s); 112836933Skarels return (p); 112910276Ssam } 113010276Ssam 113154528Sandrew static void 113236933Skarels help(ctab, s) 113336933Skarels struct tab *ctab; 113410276Ssam char *s; 113510276Ssam { 1136*66711Spendry struct tab *c; 1137*66711Spendry int width, NCMDS; 113836933Skarels char *type; 113910276Ssam 114036933Skarels if (ctab == sitetab) 114136933Skarels type = "SITE "; 114236933Skarels else 114336933Skarels type = ""; 114410276Ssam width = 0, NCMDS = 0; 114536933Skarels for (c = ctab; c->name != NULL; c++) { 114636933Skarels int len = strlen(c->name); 114710276Ssam 114810276Ssam if (len > width) 114910276Ssam width = len; 115010276Ssam NCMDS++; 115110276Ssam } 115210276Ssam width = (width + 8) &~ 7; 115310276Ssam if (s == 0) { 1154*66711Spendry int i, j, w; 115510276Ssam int columns, lines; 115610276Ssam 115736933Skarels lreply(214, "The following %scommands are recognized %s.", 115836933Skarels type, "(* =>'s unimplemented)"); 115910276Ssam columns = 76 / width; 116010276Ssam if (columns == 0) 116110276Ssam columns = 1; 116210276Ssam lines = (NCMDS + columns - 1) / columns; 116310276Ssam for (i = 0; i < lines; i++) { 116427107Smckusick printf(" "); 116510276Ssam for (j = 0; j < columns; j++) { 116636933Skarels c = ctab + j * lines + i; 116710276Ssam printf("%s%c", c->name, 116810276Ssam c->implemented ? ' ' : '*'); 116936933Skarels if (c + lines >= &ctab[NCMDS]) 117010276Ssam break; 117131132Smckusick w = strlen(c->name) + 1; 117210276Ssam while (w < width) { 117310276Ssam putchar(' '); 117410276Ssam w++; 117510276Ssam } 117610276Ssam } 117710276Ssam printf("\r\n"); 117810276Ssam } 117926494Sminshall (void) fflush(stdout); 118010276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 118110276Ssam return; 118210276Ssam } 118310276Ssam upper(s); 118436933Skarels c = lookup(ctab, s); 118510276Ssam if (c == (struct tab *)0) { 118627107Smckusick reply(502, "Unknown command %s.", s); 118710276Ssam return; 118810276Ssam } 118910276Ssam if (c->implemented) 119036933Skarels reply(214, "Syntax: %s%s %s", type, c->name, c->help); 119110276Ssam else 119236933Skarels reply(214, "%s%-*s\t%s; unimplemented.", type, width, 119336933Skarels c->name, c->help); 119410276Ssam } 119536933Skarels 119654528Sandrew static void 119736933Skarels sizecmd(filename) 119854528Sandrew char *filename; 119936933Skarels { 120036933Skarels switch (type) { 120136933Skarels case TYPE_L: 120236933Skarels case TYPE_I: { 120336933Skarels struct stat stbuf; 1204*66711Spendry if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 120536933Skarels reply(550, "%s: not a plain file.", filename); 120636933Skarels else 1207*66711Spendry reply(213, "%qu", stbuf.st_size); 1208*66711Spendry break; } 120936933Skarels case TYPE_A: { 121036933Skarels FILE *fin; 1211*66711Spendry int c; 1212*66711Spendry off_t count; 121336933Skarels struct stat stbuf; 121436933Skarels fin = fopen(filename, "r"); 121536933Skarels if (fin == NULL) { 121636933Skarels perror_reply(550, filename); 121736933Skarels return; 121836933Skarels } 1219*66711Spendry if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 122036933Skarels reply(550, "%s: not a plain file.", filename); 122136933Skarels (void) fclose(fin); 122236933Skarels return; 122336933Skarels } 122436933Skarels 122536933Skarels count = 0; 122636933Skarels while((c=getc(fin)) != EOF) { 122736933Skarels if (c == '\n') /* will get expanded to \r\n */ 122836933Skarels count++; 122936933Skarels count++; 123036933Skarels } 123136933Skarels (void) fclose(fin); 123236933Skarels 1233*66711Spendry reply(213, "%qd", count); 1234*66711Spendry break; } 123536933Skarels default: 123636933Skarels reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 123736933Skarels } 123836933Skarels } 1239