110276Ssam /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 5*42666Sbostic * %sccs.include.redist.c% 634769Sbostic * 7*42666Sbostic * @(#)ftpcmd.y 5.23 (Berkeley) 06/01/90 822501Sdist */ 922501Sdist 1022501Sdist /* 1110276Ssam * Grammar for FTP commands. 1236933Skarels * See RFC 959. 1310276Ssam */ 1410276Ssam 1510276Ssam %{ 1610276Ssam 1710276Ssam #ifndef lint 18*42666Sbostic static char sccsid[] = "@(#)ftpcmd.y 5.23 (Berkeley) 06/01/90"; 1933738Sbostic #endif /* not lint */ 2010276Ssam 2136552Sbostic #include <sys/param.h> 2210276Ssam #include <sys/socket.h> 2310276Ssam 2410276Ssam #include <netinet/in.h> 2510276Ssam 2613033Ssam #include <arpa/ftp.h> 2713033Ssam 2810276Ssam #include <stdio.h> 2911652Ssam #include <signal.h> 3010276Ssam #include <ctype.h> 3110276Ssam #include <pwd.h> 3210276Ssam #include <setjmp.h> 3326494Sminshall #include <syslog.h> 3436933Skarels #include <sys/stat.h> 3536933Skarels #include <time.h> 3610276Ssam 3710276Ssam extern struct sockaddr_in data_dest; 3810276Ssam extern int logged_in; 3910276Ssam extern struct passwd *pw; 4010276Ssam extern int guest; 4110276Ssam extern int logging; 4210276Ssam extern int type; 4310276Ssam extern int form; 4410276Ssam extern int debug; 4511652Ssam extern int timeout; 4636933Skarels extern int maxtimeout; 4726045Sminshall extern int pdata; 4836620Srick extern char hostname[], remotehost[]; 4936933Skarels extern char proctitle[]; 5010276Ssam extern char *globerr; 5110320Ssam extern int usedefault; 5226045Sminshall extern int transflag; 5326045Sminshall extern char tmpline[]; 5410276Ssam char **glob(); 5510276Ssam 5638135Srick off_t restart_point; 5738135Srick 5810276Ssam static int cmd_type; 5910276Ssam static int cmd_form; 6010276Ssam static int cmd_bytesz; 6136304Skarels char cbuf[512]; 6236304Skarels char *fromname; 6310276Ssam 6410276Ssam char *index(); 6510276Ssam %} 6610276Ssam 6710276Ssam %token 6810276Ssam A B C E F I 6910276Ssam L N P R S T 7010276Ssam 7110276Ssam SP CRLF COMMA STRING NUMBER 7210276Ssam 7310276Ssam USER PASS ACCT REIN QUIT PORT 7410276Ssam PASV TYPE STRU MODE RETR STOR 7510276Ssam APPE MLFL MAIL MSND MSOM MSAM 7610276Ssam MRSQ MRCP ALLO REST RNFR RNTO 7710276Ssam ABOR DELE CWD LIST NLST SITE 7836620Srick STAT HELP NOOP MKD RMD PWD 7936933Skarels CDUP STOU SMNT SYST SIZE MDTM 8010276Ssam 8136933Skarels UMASK IDLE CHMOD 8236933Skarels 8310276Ssam LEXERR 8410276Ssam 8510276Ssam %start cmd_list 8610276Ssam 8710276Ssam %% 8810276Ssam 8910276Ssam cmd_list: /* empty */ 9010276Ssam | cmd_list cmd 9130945Scsvsj = { 9230945Scsvsj fromname = (char *) 0; 9338135Srick restart_point = (off_t) 0; 9430945Scsvsj } 9530945Scsvsj | cmd_list rcmd 9610276Ssam ; 9710276Ssam 9810276Ssam cmd: USER SP username CRLF 9910276Ssam = { 10036304Skarels user((char *) $3); 10126494Sminshall free((char *) $3); 10210276Ssam } 10310276Ssam | PASS SP password CRLF 10410276Ssam = { 10526494Sminshall pass((char *) $3); 10626494Sminshall free((char *) $3); 10710276Ssam } 10810276Ssam | PORT SP host_port CRLF 10910276Ssam = { 11010320Ssam usedefault = 0; 11136304Skarels if (pdata >= 0) { 11226045Sminshall (void) close(pdata); 11336304Skarels pdata = -1; 11426045Sminshall } 11527107Smckusick reply(200, "PORT command successful."); 11610276Ssam } 11726045Sminshall | PASV CRLF 11826045Sminshall = { 11926045Sminshall passive(); 12026045Sminshall } 12110276Ssam | TYPE SP type_code CRLF 12210276Ssam = { 12310276Ssam switch (cmd_type) { 12410276Ssam 12510276Ssam case TYPE_A: 12610276Ssam if (cmd_form == FORM_N) { 12710276Ssam reply(200, "Type set to A."); 12810276Ssam type = cmd_type; 12910276Ssam form = cmd_form; 13010276Ssam } else 13110276Ssam reply(504, "Form must be N."); 13210276Ssam break; 13310276Ssam 13410276Ssam case TYPE_E: 13510276Ssam reply(504, "Type E not implemented."); 13610276Ssam break; 13710276Ssam 13810276Ssam case TYPE_I: 13910276Ssam reply(200, "Type set to I."); 14010276Ssam type = cmd_type; 14110276Ssam break; 14210276Ssam 14310276Ssam case TYPE_L: 14436933Skarels #if NBBY == 8 14536933Skarels if (cmd_bytesz == 8) { 14610276Ssam reply(200, 14736933Skarels "Type set to L (byte size 8)."); 14810276Ssam type = cmd_type; 14910276Ssam } else 15036933Skarels reply(504, "Byte size must be 8."); 15136933Skarels #else /* NBBY == 8 */ 15236933Skarels UNIMPLEMENTED for NBBY != 8 15336933Skarels #endif /* NBBY == 8 */ 15410276Ssam } 15510276Ssam } 15610276Ssam | STRU SP struct_code CRLF 15710276Ssam = { 15810276Ssam switch ($3) { 15910276Ssam 16010276Ssam case STRU_F: 16110276Ssam reply(200, "STRU F ok."); 16210276Ssam break; 16310276Ssam 16410276Ssam default: 16527107Smckusick reply(504, "Unimplemented STRU type."); 16610276Ssam } 16710276Ssam } 16810276Ssam | MODE SP mode_code CRLF 16910276Ssam = { 17010276Ssam switch ($3) { 17110276Ssam 17210276Ssam case MODE_S: 17310276Ssam reply(200, "MODE S ok."); 17410276Ssam break; 17510276Ssam 17610276Ssam default: 17710276Ssam reply(502, "Unimplemented MODE type."); 17810276Ssam } 17910276Ssam } 18010276Ssam | ALLO SP NUMBER CRLF 18110276Ssam = { 18227107Smckusick reply(202, "ALLO command ignored."); 18310276Ssam } 18436933Skarels | ALLO SP NUMBER SP R SP NUMBER CRLF 18536933Skarels = { 18636933Skarels reply(202, "ALLO command ignored."); 18736933Skarels } 18810276Ssam | RETR check_login SP pathname CRLF 18910276Ssam = { 19010302Ssam if ($2 && $4 != NULL) 19136933Skarels retrieve((char *) 0, (char *) $4); 19210302Ssam if ($4 != NULL) 19326494Sminshall free((char *) $4); 19410276Ssam } 19510276Ssam | STOR check_login SP pathname CRLF 19610276Ssam = { 19710302Ssam if ($2 && $4 != NULL) 19836304Skarels store((char *) $4, "w", 0); 19910302Ssam if ($4 != NULL) 20026494Sminshall free((char *) $4); 20110276Ssam } 20210276Ssam | APPE check_login SP pathname CRLF 20310276Ssam = { 20410302Ssam if ($2 && $4 != NULL) 20536304Skarels store((char *) $4, "a", 0); 20610302Ssam if ($4 != NULL) 20726494Sminshall free((char *) $4); 20810276Ssam } 20910276Ssam | NLST check_login CRLF 21010276Ssam = { 21110276Ssam if ($2) 21236620Srick send_file_list("."); 21310276Ssam } 21436620Srick | NLST check_login SP STRING CRLF 21510276Ssam = { 21636620Srick if ($2 && $4 != NULL) 21736620Srick send_file_list((char *) $4); 21810302Ssam if ($4 != NULL) 21926494Sminshall free((char *) $4); 22010276Ssam } 22110276Ssam | LIST check_login CRLF 22210276Ssam = { 22310276Ssam if ($2) 22436620Srick retrieve("/bin/ls -lgA", ""); 22510276Ssam } 22610276Ssam | LIST check_login SP pathname CRLF 22710276Ssam = { 22810302Ssam if ($2 && $4 != NULL) 22936620Srick retrieve("/bin/ls -lgA %s", (char *) $4); 23010302Ssam if ($4 != NULL) 23126494Sminshall free((char *) $4); 23210276Ssam } 23336933Skarels | STAT check_login SP pathname CRLF 23436933Skarels = { 23536933Skarels if ($2 && $4 != NULL) 23636933Skarels statfilecmd((char *) $4); 23736933Skarels if ($4 != NULL) 23836933Skarels free((char *) $4); 23936933Skarels } 24036933Skarels | STAT CRLF 24136933Skarels = { 24236933Skarels statcmd(); 24336933Skarels } 24410276Ssam | DELE check_login SP pathname CRLF 24510276Ssam = { 24610302Ssam if ($2 && $4 != NULL) 24726494Sminshall delete((char *) $4); 24810302Ssam if ($4 != NULL) 24926494Sminshall free((char *) $4); 25010276Ssam } 25130945Scsvsj | RNTO SP pathname CRLF 25230945Scsvsj = { 25330945Scsvsj if (fromname) { 25430945Scsvsj renamecmd(fromname, (char *) $3); 25530945Scsvsj free(fromname); 25630945Scsvsj fromname = (char *) 0; 25730945Scsvsj } else { 25830945Scsvsj reply(503, "Bad sequence of commands."); 25930945Scsvsj } 26030945Scsvsj free((char *) $3); 26130945Scsvsj } 26226045Sminshall | ABOR CRLF 26326045Sminshall = { 26427107Smckusick reply(225, "ABOR command successful."); 26526045Sminshall } 26610276Ssam | CWD check_login CRLF 26710276Ssam = { 26810276Ssam if ($2) 26910276Ssam cwd(pw->pw_dir); 27010276Ssam } 27110276Ssam | CWD check_login SP pathname CRLF 27210276Ssam = { 27310302Ssam if ($2 && $4 != NULL) 27426494Sminshall cwd((char *) $4); 27510302Ssam if ($4 != NULL) 27626494Sminshall free((char *) $4); 27710276Ssam } 27810276Ssam | HELP CRLF 27910276Ssam = { 28036933Skarels help(cmdtab, (char *) 0); 28110276Ssam } 28210276Ssam | HELP SP STRING CRLF 28310276Ssam = { 28436933Skarels register char *cp = (char *)$3; 28536933Skarels 28636933Skarels if (strncasecmp(cp, "SITE", 4) == 0) { 28736933Skarels cp = (char *)$3 + 4; 28836933Skarels if (*cp == ' ') 28936933Skarels cp++; 29036933Skarels if (*cp) 29136933Skarels help(sitetab, cp); 29236933Skarels else 29336933Skarels help(sitetab, (char *) 0); 29436933Skarels } else 29536933Skarels help(cmdtab, (char *) $3); 29610276Ssam } 29710276Ssam | NOOP CRLF 29810276Ssam = { 29927107Smckusick reply(200, "NOOP command successful."); 30010276Ssam } 30136620Srick | MKD check_login SP pathname CRLF 30210276Ssam = { 30310302Ssam if ($2 && $4 != NULL) 30426494Sminshall makedir((char *) $4); 30510302Ssam if ($4 != NULL) 30626494Sminshall free((char *) $4); 30710276Ssam } 30836620Srick | RMD check_login SP pathname CRLF 30910276Ssam = { 31010302Ssam if ($2 && $4 != NULL) 31126494Sminshall removedir((char *) $4); 31210302Ssam if ($4 != NULL) 31326494Sminshall free((char *) $4); 31410276Ssam } 31536620Srick | PWD check_login CRLF 31610276Ssam = { 31710276Ssam if ($2) 31810302Ssam pwd(); 31910276Ssam } 32036620Srick | CDUP check_login CRLF 32110276Ssam = { 32210276Ssam if ($2) 32310276Ssam cwd(".."); 32410276Ssam } 32536933Skarels | SITE SP HELP CRLF 32636933Skarels = { 32736933Skarels help(sitetab, (char *) 0); 32836933Skarels } 32936933Skarels | SITE SP HELP SP STRING CRLF 33036933Skarels = { 33136933Skarels help(sitetab, (char *) $5); 33236933Skarels } 33336933Skarels | SITE SP UMASK check_login CRLF 33436933Skarels = { 33536933Skarels int oldmask; 33636933Skarels 33736933Skarels if ($4) { 33836933Skarels oldmask = umask(0); 33936933Skarels (void) umask(oldmask); 34036933Skarels reply(200, "Current UMASK is %03o", oldmask); 34136933Skarels } 34236933Skarels } 34336933Skarels | SITE SP UMASK check_login SP octal_number CRLF 34436933Skarels = { 34536933Skarels int oldmask; 34636933Skarels 34736933Skarels if ($4) { 34836933Skarels if (($6 == -1) || ($6 > 0777)) { 34936933Skarels reply(501, "Bad UMASK value"); 35036933Skarels } else { 35136933Skarels oldmask = umask($6); 35236933Skarels reply(200, 35336933Skarels "UMASK set to %03o (was %03o)", 35436933Skarels $6, oldmask); 35536933Skarels } 35636933Skarels } 35736933Skarels } 35836933Skarels | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 35936933Skarels = { 36036933Skarels if ($4 && ($8 != NULL)) { 36136933Skarels if ($6 > 0777) 36236933Skarels reply(501, 36336933Skarels "CHMOD: Mode value must be between 0 and 0777"); 36436933Skarels else if (chmod((char *) $8, $6) < 0) 36536933Skarels perror_reply(550, (char *) $8); 36636933Skarels else 36736933Skarels reply(200, "CHMOD command successful."); 36836933Skarels } 36936933Skarels if ($8 != NULL) 37036933Skarels free((char *) $8); 37136933Skarels } 37236933Skarels | SITE SP IDLE CRLF 37336933Skarels = { 37436933Skarels reply(200, 37536933Skarels "Current IDLE time limit is %d seconds; max %d", 37636933Skarels timeout, maxtimeout); 37736933Skarels } 37836933Skarels | SITE SP IDLE SP NUMBER CRLF 37936933Skarels = { 38036933Skarels if ($5 < 30 || $5 > maxtimeout) { 38136933Skarels reply(501, 38236933Skarels "Maximum IDLE time must be between 30 and %d seconds", 38336933Skarels maxtimeout); 38436933Skarels } else { 38536933Skarels timeout = $5; 38636933Skarels (void) alarm((unsigned) timeout); 38736933Skarels reply(200, 38836933Skarels "Maximum IDLE time set to %d seconds", 38936933Skarels timeout); 39036933Skarels } 39136933Skarels } 39226045Sminshall | STOU check_login SP pathname CRLF 39326045Sminshall = { 39436304Skarels if ($2 && $4 != NULL) 39536304Skarels store((char *) $4, "w", 1); 39626045Sminshall if ($4 != NULL) 39726494Sminshall free((char *) $4); 39826045Sminshall } 39936552Sbostic | SYST CRLF 40036552Sbostic = { 40136640Srick #ifdef unix 40236933Skarels #ifdef BSD 40336552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 40436552Sbostic NBBY, BSD); 40536933Skarels #else /* BSD */ 40636933Skarels reply(215, "UNIX Type: L%d", NBBY); 40736933Skarels #endif /* BSD */ 40836933Skarels #else /* unix */ 40936933Skarels reply(215, "UNKNOWN Type: L%d", NBBY); 41036933Skarels #endif /* unix */ 41136552Sbostic } 41236933Skarels 41336933Skarels /* 41436933Skarels * SIZE is not in RFC959, but Postel has blessed it and 41536933Skarels * it will be in the updated RFC. 41636933Skarels * 41736933Skarels * Return size of file in a format suitable for 41836933Skarels * using with RESTART (we just count bytes). 41936933Skarels */ 42036933Skarels | SIZE check_login SP pathname CRLF 42136933Skarels = { 42236933Skarels if ($2 && $4 != NULL) 42336933Skarels sizecmd((char *) $4); 42436933Skarels if ($4 != NULL) 42536933Skarels free((char *) $4); 42636933Skarels } 42736933Skarels 42836933Skarels /* 42936933Skarels * MDTM is not in RFC959, but Postel has blessed it and 43036933Skarels * it will be in the updated RFC. 43136933Skarels * 43236933Skarels * Return modification time of file as an ISO 3307 43336933Skarels * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 43436933Skarels * where xxx is the fractional second (of any precision, 43536933Skarels * not necessarily 3 digits) 43636933Skarels */ 43736933Skarels | MDTM check_login SP pathname CRLF 43836933Skarels = { 43936933Skarels if ($2 && $4 != NULL) { 44036933Skarels struct stat stbuf; 44136933Skarels if (stat((char *) $4, &stbuf) < 0) 44236933Skarels perror_reply(550, "%s", (char *) $4); 44336933Skarels else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 44436933Skarels reply(550, "%s: not a plain file.", 44536933Skarels (char *) $4); 44636933Skarels } else { 44736933Skarels register struct tm *t; 44836933Skarels struct tm *gmtime(); 44936933Skarels t = gmtime(&stbuf.st_mtime); 45036933Skarels reply(213, 45136933Skarels "19%02d%02d%02d%02d%02d%02d", 45236933Skarels t->tm_year, t->tm_mon+1, t->tm_mday, 45336933Skarels t->tm_hour, t->tm_min, t->tm_sec); 45436933Skarels } 45536933Skarels } 45636933Skarels if ($4 != NULL) 45736933Skarels free((char *) $4); 45836933Skarels } 45910276Ssam | QUIT CRLF 46010276Ssam = { 46110276Ssam reply(221, "Goodbye."); 46213246Ssam dologout(0); 46310276Ssam } 46410276Ssam | error CRLF 46510276Ssam = { 46610276Ssam yyerrok; 46710276Ssam } 46810276Ssam ; 46930945Scsvsj rcmd: RNFR check_login SP pathname CRLF 47030945Scsvsj = { 47130945Scsvsj char *renamefrom(); 47230945Scsvsj 47338135Srick restart_point = (off_t) 0; 47430945Scsvsj if ($2 && $4) { 47530945Scsvsj fromname = renamefrom((char *) $4); 47630945Scsvsj if (fromname == (char *) 0 && $4) { 47730945Scsvsj free((char *) $4); 47830945Scsvsj } 47930945Scsvsj } 48030945Scsvsj } 48138135Srick | REST SP byte_size CRLF 48238135Srick = { 48338135Srick long atol(); 48438135Srick 48538135Srick fromname = (char *) 0; 48638135Srick restart_point = $3; 48738135Srick reply(350, "Restarting at %ld. %s", restart_point, 48838135Srick "Send STORE or RETRIEVE to initiate transfer."); 48938135Srick } 49030945Scsvsj ; 49130945Scsvsj 49210276Ssam username: STRING 49310276Ssam ; 49410276Ssam 49536304Skarels password: /* empty */ 49636304Skarels = { 49740184Smckusick *(char **)&($$) = (char *)calloc(1, sizeof(char)); 49836304Skarels } 49936304Skarels | STRING 50010276Ssam ; 50110276Ssam 50210276Ssam byte_size: NUMBER 50310276Ssam ; 50410276Ssam 50510276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 50610276Ssam NUMBER COMMA NUMBER 50710276Ssam = { 50810276Ssam register char *a, *p; 50910276Ssam 51010276Ssam a = (char *)&data_dest.sin_addr; 51110276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 51210276Ssam p = (char *)&data_dest.sin_port; 51310276Ssam p[0] = $9; p[1] = $11; 51410324Ssam data_dest.sin_family = AF_INET; 51510276Ssam } 51610276Ssam ; 51710276Ssam 51810276Ssam form_code: N 51910276Ssam = { 52010276Ssam $$ = FORM_N; 52110276Ssam } 52210276Ssam | T 52310276Ssam = { 52410276Ssam $$ = FORM_T; 52510276Ssam } 52610276Ssam | C 52710276Ssam = { 52810276Ssam $$ = FORM_C; 52910276Ssam } 53010276Ssam ; 53110276Ssam 53210276Ssam type_code: A 53310276Ssam = { 53410276Ssam cmd_type = TYPE_A; 53510276Ssam cmd_form = FORM_N; 53610276Ssam } 53710276Ssam | A SP form_code 53810276Ssam = { 53910276Ssam cmd_type = TYPE_A; 54010276Ssam cmd_form = $3; 54110276Ssam } 54210276Ssam | E 54310276Ssam = { 54410276Ssam cmd_type = TYPE_E; 54510276Ssam cmd_form = FORM_N; 54610276Ssam } 54710276Ssam | E SP form_code 54810276Ssam = { 54910276Ssam cmd_type = TYPE_E; 55010276Ssam cmd_form = $3; 55110276Ssam } 55210276Ssam | I 55310276Ssam = { 55410276Ssam cmd_type = TYPE_I; 55510276Ssam } 55610276Ssam | L 55710276Ssam = { 55810276Ssam cmd_type = TYPE_L; 55936552Sbostic cmd_bytesz = NBBY; 56010276Ssam } 56110276Ssam | L SP byte_size 56210276Ssam = { 56310276Ssam cmd_type = TYPE_L; 56410276Ssam cmd_bytesz = $3; 56510276Ssam } 56610276Ssam /* this is for a bug in the BBN ftp */ 56710276Ssam | L byte_size 56810276Ssam = { 56910276Ssam cmd_type = TYPE_L; 57010276Ssam cmd_bytesz = $2; 57110276Ssam } 57210276Ssam ; 57310276Ssam 57410276Ssam struct_code: F 57510276Ssam = { 57610276Ssam $$ = STRU_F; 57710276Ssam } 57810276Ssam | R 57910276Ssam = { 58010276Ssam $$ = STRU_R; 58110276Ssam } 58210276Ssam | P 58310276Ssam = { 58410276Ssam $$ = STRU_P; 58510276Ssam } 58610276Ssam ; 58710276Ssam 58810276Ssam mode_code: S 58910276Ssam = { 59010276Ssam $$ = MODE_S; 59110276Ssam } 59210276Ssam | B 59310276Ssam = { 59410276Ssam $$ = MODE_B; 59510276Ssam } 59610276Ssam | C 59710276Ssam = { 59810276Ssam $$ = MODE_C; 59910276Ssam } 60010276Ssam ; 60110276Ssam 60210276Ssam pathname: pathstring 60310276Ssam = { 60427107Smckusick /* 60527107Smckusick * Problem: this production is used for all pathname 60627107Smckusick * processing, but only gives a 550 error reply. 60727107Smckusick * This is a valid reply in some cases but not in others. 60827107Smckusick */ 60936304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 61036933Skarels *(char **)&($$) = *glob((char *) $1); 61110302Ssam if (globerr != NULL) { 61210276Ssam reply(550, globerr); 61310302Ssam $$ = NULL; 61410302Ssam } 61526494Sminshall free((char *) $1); 61610276Ssam } else 61710276Ssam $$ = $1; 61810276Ssam } 61910276Ssam ; 62010276Ssam 62110276Ssam pathstring: STRING 62210276Ssam ; 62310276Ssam 62436933Skarels octal_number: NUMBER 62536933Skarels = { 62636933Skarels register int ret, dec, multby, digit; 62736933Skarels 62836933Skarels /* 62936933Skarels * Convert a number that was read as decimal number 63036933Skarels * to what it would be if it had been read as octal. 63136933Skarels */ 63236933Skarels dec = $1; 63336933Skarels multby = 1; 63436933Skarels ret = 0; 63536933Skarels while (dec) { 63636933Skarels digit = dec%10; 63736933Skarels if (digit > 7) { 63836933Skarels ret = -1; 63936933Skarels break; 64036933Skarels } 64136933Skarels ret += digit * multby; 64236933Skarels multby *= 8; 64336933Skarels dec /= 10; 64436933Skarels } 64536933Skarels $$ = ret; 64636933Skarels } 64736933Skarels ; 64836933Skarels 64910276Ssam check_login: /* empty */ 65010276Ssam = { 65110276Ssam if (logged_in) 65210276Ssam $$ = 1; 65310276Ssam else { 65410276Ssam reply(530, "Please login with USER and PASS."); 65510276Ssam $$ = 0; 65610276Ssam } 65710276Ssam } 65810276Ssam ; 65910276Ssam 66010276Ssam %% 66110276Ssam 66210276Ssam extern jmp_buf errcatch; 66310276Ssam 66410276Ssam #define CMD 0 /* beginning of command */ 66510276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 66610276Ssam #define STR1 2 /* expect SP followed by STRING */ 66710276Ssam #define STR2 3 /* expect STRING */ 66836304Skarels #define OSTR 4 /* optional SP then STRING */ 66936304Skarels #define ZSTR1 5 /* SP then optional STRING */ 67036304Skarels #define ZSTR2 6 /* optional STRING after SP */ 67136933Skarels #define SITECMD 7 /* SITE command */ 67236933Skarels #define NSTR 8 /* Number followed by a string */ 67310276Ssam 67410276Ssam struct tab { 67510276Ssam char *name; 67610276Ssam short token; 67710276Ssam short state; 67810276Ssam short implemented; /* 1 if command is implemented */ 67910276Ssam char *help; 68010276Ssam }; 68110276Ssam 68210276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 68310276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 68436304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 68510276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 68636933Skarels { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 68710276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 68810276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 68910276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 69026045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 69110276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 69210276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 69310276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 69410276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 69510276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 69610276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 69710276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 69810276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 69910276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 70010276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 70110276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 70210276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 70310276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 70410276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 70538135Srick { "REST", REST, ARGS, 1, "(restart command)" }, 70610276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 70710276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 70826045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 70910276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 71036304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 71110276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 71210276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 71310276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 71436933Skarels { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 71536552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 71636933Skarels { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 71710276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 71810276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 71936620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 72036620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 72136620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 72236620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 72336620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 72436620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 72536620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 72636620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 72726045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 72836933Skarels { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 72936933Skarels { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 73010276Ssam { NULL, 0, 0, 0, 0 } 73110276Ssam }; 73210276Ssam 73336933Skarels struct tab sitetab[] = { 73436933Skarels { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 73536933Skarels { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 73636933Skarels { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 73736933Skarels { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 73836933Skarels { NULL, 0, 0, 0, 0 } 73936933Skarels }; 74036933Skarels 74110276Ssam struct tab * 74236933Skarels lookup(p, cmd) 74336933Skarels register struct tab *p; 74410276Ssam char *cmd; 74510276Ssam { 74610276Ssam 74736933Skarels for (; p->name != NULL; p++) 74810276Ssam if (strcmp(cmd, p->name) == 0) 74910276Ssam return (p); 75010276Ssam return (0); 75110276Ssam } 75210276Ssam 75313033Ssam #include <arpa/telnet.h> 75410276Ssam 75510276Ssam /* 75610276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 75710276Ssam */ 75810276Ssam char * 75910276Ssam getline(s, n, iop) 76010276Ssam char *s; 76110276Ssam register FILE *iop; 76210276Ssam { 76310276Ssam register c; 76426494Sminshall register char *cs; 76510276Ssam 76610276Ssam cs = s; 76727751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 76826045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 76926045Sminshall *cs++ = tmpline[c]; 77026045Sminshall if (tmpline[c] == '\n') { 77126045Sminshall *cs++ = '\0'; 77236304Skarels if (debug) 77336304Skarels syslog(LOG_DEBUG, "command: %s", s); 77426045Sminshall tmpline[0] = '\0'; 77526045Sminshall return(s); 77626045Sminshall } 77736304Skarels if (c == 0) 77826045Sminshall tmpline[0] = '\0'; 77926045Sminshall } 78036304Skarels while ((c = getc(iop)) != EOF) { 78136304Skarels c &= 0377; 78236304Skarels if (c == IAC) { 78336304Skarels if ((c = getc(iop)) != EOF) { 78436304Skarels c &= 0377; 78536304Skarels switch (c) { 78627751Sminshall case WILL: 78727751Sminshall case WONT: 78836277Sbostic c = getc(iop); 78936304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 79027751Sminshall (void) fflush(stdout); 79136304Skarels continue; 79227751Sminshall case DO: 79327751Sminshall case DONT: 79436277Sbostic c = getc(iop); 79536304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 79627751Sminshall (void) fflush(stdout); 79736304Skarels continue; 79836304Skarels case IAC: 79927751Sminshall break; 80027751Sminshall default: 80136304Skarels continue; /* ignore command */ 80227751Sminshall } 80336304Skarels } 80410276Ssam } 80536304Skarels *cs++ = c; 80636304Skarels if (--n <= 0 || c == '\n') 80710276Ssam break; 80810276Ssam } 80927751Sminshall if (c == EOF && cs == s) 81018303Sralph return (NULL); 81110276Ssam *cs++ = '\0'; 81236304Skarels if (debug) 81336304Skarels syslog(LOG_DEBUG, "command: %s", s); 81410276Ssam return (s); 81510276Ssam } 81610276Ssam 81711652Ssam static int 81811652Ssam toolong() 81911652Ssam { 82026494Sminshall time_t now; 82111652Ssam extern char *ctime(); 82226494Sminshall extern time_t time(); 82311652Ssam 82411652Ssam reply(421, 82511652Ssam "Timeout (%d seconds): closing control connection.", timeout); 82626494Sminshall (void) time(&now); 82711652Ssam if (logging) { 82826494Sminshall syslog(LOG_INFO, 82936304Skarels "User %s timed out after %d seconds at %s", 83011652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 83111652Ssam } 83213246Ssam dologout(1); 83311652Ssam } 83411652Ssam 83510276Ssam yylex() 83610276Ssam { 83710276Ssam static int cpos, state; 83836933Skarels register char *cp, *cp2; 83910276Ssam register struct tab *p; 84010276Ssam int n; 84136277Sbostic char c, *strpbrk(); 84236933Skarels char *copy(); 84310276Ssam 84410276Ssam for (;;) { 84510276Ssam switch (state) { 84610276Ssam 84710276Ssam case CMD: 84826494Sminshall (void) signal(SIGALRM, toolong); 84926494Sminshall (void) alarm((unsigned) timeout); 85010276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 85110276Ssam reply(221, "You could at least say goodbye."); 85213246Ssam dologout(0); 85310276Ssam } 85426494Sminshall (void) alarm(0); 85536620Srick #ifdef SETPROCTITLE 85636933Skarels if (strncasecmp(cbuf, "PASS", 4) != NULL) 85736933Skarels setproctitle("%s: %s", proctitle, cbuf); 85836620Srick #endif /* SETPROCTITLE */ 85936324Sbostic if ((cp = index(cbuf, '\r'))) { 86036324Sbostic *cp++ = '\n'; 86136324Sbostic *cp = '\0'; 86236324Sbostic } 86336277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 86436277Sbostic cpos = cp - cbuf; 86536304Skarels if (cpos == 0) 86610276Ssam cpos = 4; 86710276Ssam c = cbuf[cpos]; 86810276Ssam cbuf[cpos] = '\0'; 86910276Ssam upper(cbuf); 87036933Skarels p = lookup(cmdtab, cbuf); 87110276Ssam cbuf[cpos] = c; 87210276Ssam if (p != 0) { 87310276Ssam if (p->implemented == 0) { 87410276Ssam nack(p->name); 87526494Sminshall longjmp(errcatch,0); 87610276Ssam /* NOTREACHED */ 87710276Ssam } 87810276Ssam state = p->state; 87936933Skarels *(char **)&yylval = p->name; 88010276Ssam return (p->token); 88110276Ssam } 88210276Ssam break; 88310276Ssam 88436933Skarels case SITECMD: 88536933Skarels if (cbuf[cpos] == ' ') { 88636933Skarels cpos++; 88736933Skarels return (SP); 88836933Skarels } 88936933Skarels cp = &cbuf[cpos]; 89036933Skarels if ((cp2 = strpbrk(cp, " \n"))) 89136933Skarels cpos = cp2 - cbuf; 89236933Skarels c = cbuf[cpos]; 89336933Skarels cbuf[cpos] = '\0'; 89436933Skarels upper(cp); 89536933Skarels p = lookup(sitetab, cp); 89636933Skarels cbuf[cpos] = c; 89736933Skarels if (p != 0) { 89836933Skarels if (p->implemented == 0) { 89936933Skarels state = CMD; 90036933Skarels nack(p->name); 90136933Skarels longjmp(errcatch,0); 90236933Skarels /* NOTREACHED */ 90336933Skarels } 90436933Skarels state = p->state; 90536933Skarels *(char **)&yylval = p->name; 90636933Skarels return (p->token); 90736933Skarels } 90836933Skarels state = CMD; 90936933Skarels break; 91036933Skarels 91110276Ssam case OSTR: 91210276Ssam if (cbuf[cpos] == '\n') { 91310276Ssam state = CMD; 91410276Ssam return (CRLF); 91510276Ssam } 91636317Sbostic /* FALLTHROUGH */ 91710276Ssam 91810276Ssam case STR1: 91936304Skarels case ZSTR1: 92036933Skarels dostr1: 92110276Ssam if (cbuf[cpos] == ' ') { 92210276Ssam cpos++; 92336317Sbostic state = state == OSTR ? STR2 : ++state; 92410276Ssam return (SP); 92510276Ssam } 92610276Ssam break; 92710276Ssam 92836304Skarels case ZSTR2: 92936304Skarels if (cbuf[cpos] == '\n') { 93036304Skarels state = CMD; 93136304Skarels return (CRLF); 93236304Skarels } 93336933Skarels /* FALLTHROUGH */ 93436304Skarels 93510276Ssam case STR2: 93610276Ssam cp = &cbuf[cpos]; 93710276Ssam n = strlen(cp); 93810276Ssam cpos += n - 1; 93910276Ssam /* 94010276Ssam * Make sure the string is nonempty and \n terminated. 94110276Ssam */ 94210276Ssam if (n > 1 && cbuf[cpos] == '\n') { 94310276Ssam cbuf[cpos] = '\0'; 94436933Skarels *(char **)&yylval = copy(cp); 94510276Ssam cbuf[cpos] = '\n'; 94610276Ssam state = ARGS; 94710276Ssam return (STRING); 94810276Ssam } 94910276Ssam break; 95010276Ssam 95136933Skarels case NSTR: 95236933Skarels if (cbuf[cpos] == ' ') { 95336933Skarels cpos++; 95436933Skarels return (SP); 95536933Skarels } 95636933Skarels if (isdigit(cbuf[cpos])) { 95736933Skarels cp = &cbuf[cpos]; 95836933Skarels while (isdigit(cbuf[++cpos])) 95936933Skarels ; 96036933Skarels c = cbuf[cpos]; 96136933Skarels cbuf[cpos] = '\0'; 96236933Skarels yylval = atoi(cp); 96336933Skarels cbuf[cpos] = c; 96436933Skarels state = STR1; 96536933Skarels return (NUMBER); 96636933Skarels } 96736933Skarels state = STR1; 96836933Skarels goto dostr1; 96936933Skarels 97010276Ssam case ARGS: 97110276Ssam if (isdigit(cbuf[cpos])) { 97210276Ssam cp = &cbuf[cpos]; 97310276Ssam while (isdigit(cbuf[++cpos])) 97410276Ssam ; 97510276Ssam c = cbuf[cpos]; 97610276Ssam cbuf[cpos] = '\0'; 97710276Ssam yylval = atoi(cp); 97810276Ssam cbuf[cpos] = c; 97910276Ssam return (NUMBER); 98010276Ssam } 98110276Ssam switch (cbuf[cpos++]) { 98210276Ssam 98310276Ssam case '\n': 98410276Ssam state = CMD; 98510276Ssam return (CRLF); 98610276Ssam 98710276Ssam case ' ': 98810276Ssam return (SP); 98910276Ssam 99010276Ssam case ',': 99110276Ssam return (COMMA); 99210276Ssam 99310276Ssam case 'A': 99410276Ssam case 'a': 99510276Ssam return (A); 99610276Ssam 99710276Ssam case 'B': 99810276Ssam case 'b': 99910276Ssam return (B); 100010276Ssam 100110276Ssam case 'C': 100210276Ssam case 'c': 100310276Ssam return (C); 100410276Ssam 100510276Ssam case 'E': 100610276Ssam case 'e': 100710276Ssam return (E); 100810276Ssam 100910276Ssam case 'F': 101010276Ssam case 'f': 101110276Ssam return (F); 101210276Ssam 101310276Ssam case 'I': 101410276Ssam case 'i': 101510276Ssam return (I); 101610276Ssam 101710276Ssam case 'L': 101810276Ssam case 'l': 101910276Ssam return (L); 102010276Ssam 102110276Ssam case 'N': 102210276Ssam case 'n': 102310276Ssam return (N); 102410276Ssam 102510276Ssam case 'P': 102610276Ssam case 'p': 102710276Ssam return (P); 102810276Ssam 102910276Ssam case 'R': 103010276Ssam case 'r': 103110276Ssam return (R); 103210276Ssam 103310276Ssam case 'S': 103410276Ssam case 's': 103510276Ssam return (S); 103610276Ssam 103710276Ssam case 'T': 103810276Ssam case 't': 103910276Ssam return (T); 104010276Ssam 104110276Ssam } 104210276Ssam break; 104310276Ssam 104410276Ssam default: 104510276Ssam fatal("Unknown state in scanner."); 104610276Ssam } 104726494Sminshall yyerror((char *) 0); 104810276Ssam state = CMD; 104926494Sminshall longjmp(errcatch,0); 105010276Ssam } 105110276Ssam } 105210276Ssam 105310276Ssam upper(s) 105436277Sbostic register char *s; 105510276Ssam { 105610276Ssam while (*s != '\0') { 105710276Ssam if (islower(*s)) 105810276Ssam *s = toupper(*s); 105910276Ssam s++; 106010276Ssam } 106110276Ssam } 106210276Ssam 106336933Skarels char * 106410276Ssam copy(s) 106510276Ssam char *s; 106610276Ssam { 106710276Ssam char *p; 106826494Sminshall extern char *malloc(), *strcpy(); 106910276Ssam 107026494Sminshall p = malloc((unsigned) strlen(s) + 1); 107110276Ssam if (p == NULL) 107210276Ssam fatal("Ran out of memory."); 107326494Sminshall (void) strcpy(p, s); 107436933Skarels return (p); 107510276Ssam } 107610276Ssam 107736933Skarels help(ctab, s) 107836933Skarels struct tab *ctab; 107910276Ssam char *s; 108010276Ssam { 108110276Ssam register struct tab *c; 108210276Ssam register int width, NCMDS; 108336933Skarels char *type; 108410276Ssam 108536933Skarels if (ctab == sitetab) 108636933Skarels type = "SITE "; 108736933Skarels else 108836933Skarels type = ""; 108910276Ssam width = 0, NCMDS = 0; 109036933Skarels for (c = ctab; c->name != NULL; c++) { 109136933Skarels int len = strlen(c->name); 109210276Ssam 109310276Ssam if (len > width) 109410276Ssam width = len; 109510276Ssam NCMDS++; 109610276Ssam } 109710276Ssam width = (width + 8) &~ 7; 109810276Ssam if (s == 0) { 109910276Ssam register int i, j, w; 110010276Ssam int columns, lines; 110110276Ssam 110236933Skarels lreply(214, "The following %scommands are recognized %s.", 110336933Skarels type, "(* =>'s unimplemented)"); 110410276Ssam columns = 76 / width; 110510276Ssam if (columns == 0) 110610276Ssam columns = 1; 110710276Ssam lines = (NCMDS + columns - 1) / columns; 110810276Ssam for (i = 0; i < lines; i++) { 110927107Smckusick printf(" "); 111010276Ssam for (j = 0; j < columns; j++) { 111136933Skarels c = ctab + j * lines + i; 111210276Ssam printf("%s%c", c->name, 111310276Ssam c->implemented ? ' ' : '*'); 111436933Skarels if (c + lines >= &ctab[NCMDS]) 111510276Ssam break; 111631132Smckusick w = strlen(c->name) + 1; 111710276Ssam while (w < width) { 111810276Ssam putchar(' '); 111910276Ssam w++; 112010276Ssam } 112110276Ssam } 112210276Ssam printf("\r\n"); 112310276Ssam } 112426494Sminshall (void) fflush(stdout); 112510276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 112610276Ssam return; 112710276Ssam } 112810276Ssam upper(s); 112936933Skarels c = lookup(ctab, s); 113010276Ssam if (c == (struct tab *)0) { 113127107Smckusick reply(502, "Unknown command %s.", s); 113210276Ssam return; 113310276Ssam } 113410276Ssam if (c->implemented) 113536933Skarels reply(214, "Syntax: %s%s %s", type, c->name, c->help); 113610276Ssam else 113736933Skarels reply(214, "%s%-*s\t%s; unimplemented.", type, width, 113836933Skarels c->name, c->help); 113910276Ssam } 114036933Skarels 114136933Skarels sizecmd(filename) 114236933Skarels char *filename; 114336933Skarels { 114436933Skarels switch (type) { 114536933Skarels case TYPE_L: 114636933Skarels case TYPE_I: { 114736933Skarels struct stat stbuf; 114836933Skarels if (stat(filename, &stbuf) < 0 || 114936933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) 115036933Skarels reply(550, "%s: not a plain file.", filename); 115136933Skarels else 115236933Skarels reply(213, "%lu", stbuf.st_size); 115336933Skarels break;} 115436933Skarels case TYPE_A: { 115536933Skarels FILE *fin; 115638135Srick register int c; 115738135Srick register long count; 115836933Skarels struct stat stbuf; 115936933Skarels fin = fopen(filename, "r"); 116036933Skarels if (fin == NULL) { 116136933Skarels perror_reply(550, filename); 116236933Skarels return; 116336933Skarels } 116436933Skarels if (fstat(fileno(fin), &stbuf) < 0 || 116536933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) { 116636933Skarels reply(550, "%s: not a plain file.", filename); 116736933Skarels (void) fclose(fin); 116836933Skarels return; 116936933Skarels } 117036933Skarels 117136933Skarels count = 0; 117236933Skarels while((c=getc(fin)) != EOF) { 117336933Skarels if (c == '\n') /* will get expanded to \r\n */ 117436933Skarels count++; 117536933Skarels count++; 117636933Skarels } 117736933Skarels (void) fclose(fin); 117836933Skarels 117936933Skarels reply(213, "%ld", count); 118036933Skarels break;} 118136933Skarels default: 118236933Skarels reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 118336933Skarels } 118436933Skarels } 1185