110276Ssam /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 634769Sbostic * 7*52999Sbostic * @(#)ftpcmd.y 5.25 (Berkeley) 03/18/92 822501Sdist */ 922501Sdist 1022501Sdist /* 1110276Ssam * Grammar for FTP commands. 1236933Skarels * See RFC 959. 1310276Ssam */ 1410276Ssam 1510276Ssam %{ 1610276Ssam 1710276Ssam #ifndef lint 18*52999Sbostic static char sccsid[] = "@(#)ftpcmd.y 5.25 (Berkeley) 03/18/92"; 1933738Sbostic #endif /* not lint */ 2010276Ssam 2136552Sbostic #include <sys/param.h> 2210276Ssam #include <sys/socket.h> 2346669Sbostic #include <sys/stat.h> 2410276Ssam #include <netinet/in.h> 2513033Ssam #include <arpa/ftp.h> 2611652Ssam #include <signal.h> 2710276Ssam #include <setjmp.h> 2826494Sminshall #include <syslog.h> 2936933Skarels #include <time.h> 3046669Sbostic #include <pwd.h> 3146669Sbostic #include <unistd.h> 3246669Sbostic #include <stdio.h> 3346669Sbostic #include <ctype.h> 3446669Sbostic #include <stdlib.h> 3546669Sbostic #include <string.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[]; 5446669Sbostic char **ftpglob(); 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 %} 6510276Ssam 6610276Ssam %token 6710276Ssam A B C E F I 6810276Ssam L N P R S T 6910276Ssam 7010276Ssam SP CRLF COMMA STRING NUMBER 7110276Ssam 7210276Ssam USER PASS ACCT REIN QUIT PORT 7310276Ssam PASV TYPE STRU MODE RETR STOR 7410276Ssam APPE MLFL MAIL MSND MSOM MSAM 7510276Ssam MRSQ MRCP ALLO REST RNFR RNTO 7610276Ssam ABOR DELE CWD LIST NLST SITE 7736620Srick STAT HELP NOOP MKD RMD PWD 7836933Skarels CDUP STOU SMNT SYST SIZE MDTM 7910276Ssam 8036933Skarels UMASK IDLE CHMOD 8136933Skarels 8210276Ssam LEXERR 8310276Ssam 8410276Ssam %start cmd_list 8510276Ssam 8610276Ssam %% 8710276Ssam 8810276Ssam cmd_list: /* empty */ 8910276Ssam | cmd_list cmd 9030945Scsvsj = { 9130945Scsvsj fromname = (char *) 0; 9238135Srick restart_point = (off_t) 0; 9330945Scsvsj } 9430945Scsvsj | cmd_list rcmd 9510276Ssam ; 9610276Ssam 9710276Ssam cmd: USER SP username CRLF 9810276Ssam = { 9936304Skarels user((char *) $3); 10026494Sminshall free((char *) $3); 10110276Ssam } 10210276Ssam | PASS SP password CRLF 10310276Ssam = { 10426494Sminshall pass((char *) $3); 10526494Sminshall free((char *) $3); 10610276Ssam } 10710276Ssam | PORT SP host_port CRLF 10810276Ssam = { 10910320Ssam usedefault = 0; 11036304Skarels if (pdata >= 0) { 11126045Sminshall (void) close(pdata); 11236304Skarels pdata = -1; 11326045Sminshall } 11427107Smckusick reply(200, "PORT command successful."); 11510276Ssam } 11626045Sminshall | PASV CRLF 11726045Sminshall = { 11826045Sminshall passive(); 11926045Sminshall } 12010276Ssam | TYPE SP type_code CRLF 12110276Ssam = { 12210276Ssam switch (cmd_type) { 12310276Ssam 12410276Ssam case TYPE_A: 12510276Ssam if (cmd_form == FORM_N) { 12610276Ssam reply(200, "Type set to A."); 12710276Ssam type = cmd_type; 12810276Ssam form = cmd_form; 12910276Ssam } else 13010276Ssam reply(504, "Form must be N."); 13110276Ssam break; 13210276Ssam 13310276Ssam case TYPE_E: 13410276Ssam reply(504, "Type E not implemented."); 13510276Ssam break; 13610276Ssam 13710276Ssam case TYPE_I: 13810276Ssam reply(200, "Type set to I."); 13910276Ssam type = cmd_type; 14010276Ssam break; 14110276Ssam 14210276Ssam case TYPE_L: 14336933Skarels #if NBBY == 8 14436933Skarels if (cmd_bytesz == 8) { 14510276Ssam reply(200, 14636933Skarels "Type set to L (byte size 8)."); 14710276Ssam type = cmd_type; 14810276Ssam } else 14936933Skarels reply(504, "Byte size must be 8."); 15036933Skarels #else /* NBBY == 8 */ 15136933Skarels UNIMPLEMENTED for NBBY != 8 15236933Skarels #endif /* NBBY == 8 */ 15310276Ssam } 15410276Ssam } 15510276Ssam | STRU SP struct_code CRLF 15610276Ssam = { 15710276Ssam switch ($3) { 15810276Ssam 15910276Ssam case STRU_F: 16010276Ssam reply(200, "STRU F ok."); 16110276Ssam break; 16210276Ssam 16310276Ssam default: 16427107Smckusick reply(504, "Unimplemented STRU type."); 16510276Ssam } 16610276Ssam } 16710276Ssam | MODE SP mode_code CRLF 16810276Ssam = { 16910276Ssam switch ($3) { 17010276Ssam 17110276Ssam case MODE_S: 17210276Ssam reply(200, "MODE S ok."); 17310276Ssam break; 17410276Ssam 17510276Ssam default: 17610276Ssam reply(502, "Unimplemented MODE type."); 17710276Ssam } 17810276Ssam } 17910276Ssam | ALLO SP NUMBER CRLF 18010276Ssam = { 18127107Smckusick reply(202, "ALLO command ignored."); 18210276Ssam } 18336933Skarels | ALLO SP NUMBER SP R SP NUMBER CRLF 18436933Skarels = { 18536933Skarels reply(202, "ALLO command ignored."); 18636933Skarels } 18710276Ssam | RETR check_login SP pathname CRLF 18810276Ssam = { 18910302Ssam if ($2 && $4 != NULL) 19036933Skarels retrieve((char *) 0, (char *) $4); 19110302Ssam if ($4 != NULL) 19226494Sminshall free((char *) $4); 19310276Ssam } 19410276Ssam | STOR check_login SP pathname CRLF 19510276Ssam = { 19610302Ssam if ($2 && $4 != NULL) 19736304Skarels store((char *) $4, "w", 0); 19810302Ssam if ($4 != NULL) 19926494Sminshall free((char *) $4); 20010276Ssam } 20110276Ssam | APPE check_login SP pathname CRLF 20210276Ssam = { 20310302Ssam if ($2 && $4 != NULL) 20436304Skarels store((char *) $4, "a", 0); 20510302Ssam if ($4 != NULL) 20626494Sminshall free((char *) $4); 20710276Ssam } 20810276Ssam | NLST check_login CRLF 20910276Ssam = { 21010276Ssam if ($2) 21136620Srick send_file_list("."); 21210276Ssam } 21336620Srick | NLST check_login SP STRING CRLF 21410276Ssam = { 215*52999Sbostic if ($2 && $4 != NULL) 21636620Srick send_file_list((char *) $4); 21710302Ssam if ($4 != NULL) 21826494Sminshall free((char *) $4); 21910276Ssam } 22010276Ssam | LIST check_login CRLF 22110276Ssam = { 22210276Ssam if ($2) 22336620Srick retrieve("/bin/ls -lgA", ""); 22410276Ssam } 22510276Ssam | LIST check_login SP pathname CRLF 22610276Ssam = { 22710302Ssam if ($2 && $4 != NULL) 22836620Srick retrieve("/bin/ls -lgA %s", (char *) $4); 22910302Ssam if ($4 != NULL) 23026494Sminshall free((char *) $4); 23110276Ssam } 23236933Skarels | STAT check_login SP pathname CRLF 23336933Skarels = { 23436933Skarels if ($2 && $4 != NULL) 23536933Skarels statfilecmd((char *) $4); 23636933Skarels if ($4 != NULL) 23736933Skarels free((char *) $4); 23836933Skarels } 23936933Skarels | STAT CRLF 24036933Skarels = { 24136933Skarels statcmd(); 24236933Skarels } 24310276Ssam | DELE check_login SP pathname CRLF 24410276Ssam = { 24510302Ssam if ($2 && $4 != NULL) 24626494Sminshall delete((char *) $4); 24710302Ssam if ($4 != NULL) 24826494Sminshall free((char *) $4); 24910276Ssam } 25030945Scsvsj | RNTO SP pathname CRLF 25130945Scsvsj = { 25230945Scsvsj if (fromname) { 25330945Scsvsj renamecmd(fromname, (char *) $3); 25430945Scsvsj free(fromname); 25530945Scsvsj fromname = (char *) 0; 25630945Scsvsj } else { 25730945Scsvsj reply(503, "Bad sequence of commands."); 25830945Scsvsj } 25930945Scsvsj free((char *) $3); 26030945Scsvsj } 26126045Sminshall | ABOR CRLF 26226045Sminshall = { 26327107Smckusick reply(225, "ABOR command successful."); 26426045Sminshall } 26510276Ssam | CWD check_login CRLF 26610276Ssam = { 26710276Ssam if ($2) 26810276Ssam cwd(pw->pw_dir); 26910276Ssam } 27010276Ssam | CWD check_login SP pathname CRLF 27110276Ssam = { 27210302Ssam if ($2 && $4 != NULL) 27326494Sminshall cwd((char *) $4); 27410302Ssam if ($4 != NULL) 27526494Sminshall free((char *) $4); 27610276Ssam } 27710276Ssam | HELP CRLF 27810276Ssam = { 27936933Skarels help(cmdtab, (char *) 0); 28010276Ssam } 28110276Ssam | HELP SP STRING CRLF 28210276Ssam = { 28336933Skarels register char *cp = (char *)$3; 28436933Skarels 28536933Skarels if (strncasecmp(cp, "SITE", 4) == 0) { 28636933Skarels cp = (char *)$3 + 4; 28736933Skarels if (*cp == ' ') 28836933Skarels cp++; 28936933Skarels if (*cp) 29036933Skarels help(sitetab, cp); 29136933Skarels else 29236933Skarels help(sitetab, (char *) 0); 29336933Skarels } else 29436933Skarels help(cmdtab, (char *) $3); 29510276Ssam } 29610276Ssam | NOOP CRLF 29710276Ssam = { 29827107Smckusick reply(200, "NOOP command successful."); 29910276Ssam } 30036620Srick | MKD check_login SP pathname CRLF 30110276Ssam = { 30210302Ssam if ($2 && $4 != NULL) 30326494Sminshall makedir((char *) $4); 30410302Ssam if ($4 != NULL) 30526494Sminshall free((char *) $4); 30610276Ssam } 30736620Srick | RMD check_login SP pathname CRLF 30810276Ssam = { 30910302Ssam if ($2 && $4 != NULL) 31026494Sminshall removedir((char *) $4); 31110302Ssam if ($4 != NULL) 31226494Sminshall free((char *) $4); 31310276Ssam } 31436620Srick | PWD check_login CRLF 31510276Ssam = { 31610276Ssam if ($2) 31710302Ssam pwd(); 31810276Ssam } 31936620Srick | CDUP check_login CRLF 32010276Ssam = { 32110276Ssam if ($2) 32210276Ssam cwd(".."); 32310276Ssam } 32436933Skarels | SITE SP HELP CRLF 32536933Skarels = { 32636933Skarels help(sitetab, (char *) 0); 32736933Skarels } 32836933Skarels | SITE SP HELP SP STRING CRLF 32936933Skarels = { 33036933Skarels help(sitetab, (char *) $5); 33136933Skarels } 33236933Skarels | SITE SP UMASK check_login CRLF 33336933Skarels = { 33436933Skarels int oldmask; 33536933Skarels 33636933Skarels if ($4) { 33736933Skarels oldmask = umask(0); 33836933Skarels (void) umask(oldmask); 33936933Skarels reply(200, "Current UMASK is %03o", oldmask); 34036933Skarels } 34136933Skarels } 34236933Skarels | SITE SP UMASK check_login SP octal_number CRLF 34336933Skarels = { 34436933Skarels int oldmask; 34536933Skarels 34636933Skarels if ($4) { 34736933Skarels if (($6 == -1) || ($6 > 0777)) { 34836933Skarels reply(501, "Bad UMASK value"); 34936933Skarels } else { 35036933Skarels oldmask = umask($6); 35136933Skarels reply(200, 35236933Skarels "UMASK set to %03o (was %03o)", 35336933Skarels $6, oldmask); 35436933Skarels } 35536933Skarels } 35636933Skarels } 35736933Skarels | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 35836933Skarels = { 35936933Skarels if ($4 && ($8 != NULL)) { 36036933Skarels if ($6 > 0777) 36136933Skarels reply(501, 36236933Skarels "CHMOD: Mode value must be between 0 and 0777"); 36336933Skarels else if (chmod((char *) $8, $6) < 0) 36436933Skarels perror_reply(550, (char *) $8); 36536933Skarels else 36636933Skarels reply(200, "CHMOD command successful."); 36736933Skarels } 36836933Skarels if ($8 != NULL) 36936933Skarels free((char *) $8); 37036933Skarels } 37136933Skarels | SITE SP IDLE CRLF 37236933Skarels = { 37336933Skarels reply(200, 37436933Skarels "Current IDLE time limit is %d seconds; max %d", 37536933Skarels timeout, maxtimeout); 37636933Skarels } 37736933Skarels | SITE SP IDLE SP NUMBER CRLF 37836933Skarels = { 37936933Skarels if ($5 < 30 || $5 > maxtimeout) { 38036933Skarels reply(501, 38136933Skarels "Maximum IDLE time must be between 30 and %d seconds", 38236933Skarels maxtimeout); 38336933Skarels } else { 38436933Skarels timeout = $5; 38536933Skarels (void) alarm((unsigned) timeout); 38636933Skarels reply(200, 38736933Skarels "Maximum IDLE time set to %d seconds", 38836933Skarels timeout); 38936933Skarels } 39036933Skarels } 39126045Sminshall | STOU check_login SP pathname CRLF 39226045Sminshall = { 39336304Skarels if ($2 && $4 != NULL) 39436304Skarels store((char *) $4, "w", 1); 39526045Sminshall if ($4 != NULL) 39626494Sminshall free((char *) $4); 39726045Sminshall } 39836552Sbostic | SYST CRLF 39936552Sbostic = { 40036640Srick #ifdef unix 40136933Skarels #ifdef BSD 40236552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 40336552Sbostic NBBY, BSD); 40436933Skarels #else /* BSD */ 40536933Skarels reply(215, "UNIX Type: L%d", NBBY); 40636933Skarels #endif /* BSD */ 40736933Skarels #else /* unix */ 40836933Skarels reply(215, "UNKNOWN Type: L%d", NBBY); 40936933Skarels #endif /* unix */ 41036552Sbostic } 41136933Skarels 41236933Skarels /* 41336933Skarels * SIZE is not in RFC959, but Postel has blessed it and 41436933Skarels * it will be in the updated RFC. 41536933Skarels * 41636933Skarels * Return size of file in a format suitable for 41736933Skarels * using with RESTART (we just count bytes). 41836933Skarels */ 41936933Skarels | SIZE check_login SP pathname CRLF 42036933Skarels = { 42136933Skarels if ($2 && $4 != NULL) 42236933Skarels sizecmd((char *) $4); 42336933Skarels if ($4 != NULL) 42436933Skarels free((char *) $4); 42536933Skarels } 42636933Skarels 42736933Skarels /* 42836933Skarels * MDTM is not in RFC959, but Postel has blessed it and 42936933Skarels * it will be in the updated RFC. 43036933Skarels * 43136933Skarels * Return modification time of file as an ISO 3307 43236933Skarels * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 43336933Skarels * where xxx is the fractional second (of any precision, 43436933Skarels * not necessarily 3 digits) 43536933Skarels */ 43636933Skarels | MDTM check_login SP pathname CRLF 43736933Skarels = { 43836933Skarels if ($2 && $4 != NULL) { 43936933Skarels struct stat stbuf; 44036933Skarels if (stat((char *) $4, &stbuf) < 0) 44136933Skarels perror_reply(550, "%s", (char *) $4); 44236933Skarels else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 44336933Skarels reply(550, "%s: not a plain file.", 44436933Skarels (char *) $4); 44536933Skarels } else { 44636933Skarels register struct tm *t; 44736933Skarels struct tm *gmtime(); 44836933Skarels t = gmtime(&stbuf.st_mtime); 44936933Skarels reply(213, 45036933Skarels "19%02d%02d%02d%02d%02d%02d", 45136933Skarels t->tm_year, t->tm_mon+1, t->tm_mday, 45236933Skarels t->tm_hour, t->tm_min, t->tm_sec); 45336933Skarels } 45436933Skarels } 45536933Skarels if ($4 != NULL) 45636933Skarels free((char *) $4); 45736933Skarels } 45810276Ssam | QUIT CRLF 45910276Ssam = { 46010276Ssam reply(221, "Goodbye."); 46113246Ssam dologout(0); 46210276Ssam } 46310276Ssam | error CRLF 46410276Ssam = { 46510276Ssam yyerrok; 46610276Ssam } 46710276Ssam ; 46830945Scsvsj rcmd: RNFR check_login SP pathname CRLF 46930945Scsvsj = { 47030945Scsvsj char *renamefrom(); 47130945Scsvsj 47238135Srick restart_point = (off_t) 0; 47330945Scsvsj if ($2 && $4) { 47430945Scsvsj fromname = renamefrom((char *) $4); 47530945Scsvsj if (fromname == (char *) 0 && $4) { 47630945Scsvsj free((char *) $4); 47730945Scsvsj } 47830945Scsvsj } 47930945Scsvsj } 48038135Srick | REST SP byte_size CRLF 48138135Srick = { 48238135Srick long atol(); 48338135Srick 48438135Srick fromname = (char *) 0; 48538135Srick restart_point = $3; 48638135Srick reply(350, "Restarting at %ld. %s", restart_point, 48738135Srick "Send STORE or RETRIEVE to initiate transfer."); 48838135Srick } 48930945Scsvsj ; 490*52999Sbostic 49110276Ssam username: STRING 49210276Ssam ; 49310276Ssam 49436304Skarels password: /* empty */ 49536304Skarels = { 49640184Smckusick *(char **)&($$) = (char *)calloc(1, sizeof(char)); 49736304Skarels } 49836304Skarels | STRING 49910276Ssam ; 50010276Ssam 50110276Ssam byte_size: NUMBER 50210276Ssam ; 50310276Ssam 504*52999Sbostic host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 50510276Ssam NUMBER COMMA NUMBER 50610276Ssam = { 50710276Ssam register char *a, *p; 50810276Ssam 50910276Ssam a = (char *)&data_dest.sin_addr; 51010276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 51110276Ssam p = (char *)&data_dest.sin_port; 51210276Ssam p[0] = $9; p[1] = $11; 51310324Ssam data_dest.sin_family = AF_INET; 51410276Ssam } 51510276Ssam ; 51610276Ssam 51710276Ssam form_code: N 51810276Ssam = { 51910276Ssam $$ = FORM_N; 52010276Ssam } 52110276Ssam | T 52210276Ssam = { 52310276Ssam $$ = FORM_T; 52410276Ssam } 52510276Ssam | C 52610276Ssam = { 52710276Ssam $$ = FORM_C; 52810276Ssam } 52910276Ssam ; 53010276Ssam 53110276Ssam type_code: A 53210276Ssam = { 53310276Ssam cmd_type = TYPE_A; 53410276Ssam cmd_form = FORM_N; 53510276Ssam } 53610276Ssam | A SP form_code 53710276Ssam = { 53810276Ssam cmd_type = TYPE_A; 53910276Ssam cmd_form = $3; 54010276Ssam } 54110276Ssam | E 54210276Ssam = { 54310276Ssam cmd_type = TYPE_E; 54410276Ssam cmd_form = FORM_N; 54510276Ssam } 54610276Ssam | E SP form_code 54710276Ssam = { 54810276Ssam cmd_type = TYPE_E; 54910276Ssam cmd_form = $3; 55010276Ssam } 55110276Ssam | I 55210276Ssam = { 55310276Ssam cmd_type = TYPE_I; 55410276Ssam } 55510276Ssam | L 55610276Ssam = { 55710276Ssam cmd_type = TYPE_L; 55836552Sbostic cmd_bytesz = NBBY; 55910276Ssam } 56010276Ssam | L SP byte_size 56110276Ssam = { 56210276Ssam cmd_type = TYPE_L; 56310276Ssam cmd_bytesz = $3; 56410276Ssam } 56510276Ssam /* this is for a bug in the BBN ftp */ 56610276Ssam | L byte_size 56710276Ssam = { 56810276Ssam cmd_type = TYPE_L; 56910276Ssam cmd_bytesz = $2; 57010276Ssam } 57110276Ssam ; 57210276Ssam 57310276Ssam struct_code: F 57410276Ssam = { 57510276Ssam $$ = STRU_F; 57610276Ssam } 57710276Ssam | R 57810276Ssam = { 57910276Ssam $$ = STRU_R; 58010276Ssam } 58110276Ssam | P 58210276Ssam = { 58310276Ssam $$ = STRU_P; 58410276Ssam } 58510276Ssam ; 58610276Ssam 58710276Ssam mode_code: S 58810276Ssam = { 58910276Ssam $$ = MODE_S; 59010276Ssam } 59110276Ssam | B 59210276Ssam = { 59310276Ssam $$ = MODE_B; 59410276Ssam } 59510276Ssam | C 59610276Ssam = { 59710276Ssam $$ = MODE_C; 59810276Ssam } 59910276Ssam ; 60010276Ssam 60110276Ssam pathname: pathstring 60210276Ssam = { 60327107Smckusick /* 60427107Smckusick * Problem: this production is used for all pathname 60527107Smckusick * processing, but only gives a 550 error reply. 60627107Smckusick * This is a valid reply in some cases but not in others. 60727107Smckusick */ 60836304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 60946669Sbostic *(char **)&($$) = *ftpglob((char *) $1); 61010302Ssam if (globerr != NULL) { 61110276Ssam reply(550, globerr); 61210302Ssam $$ = NULL; 61310302Ssam } 61426494Sminshall free((char *) $1); 61510276Ssam } else 61610276Ssam $$ = $1; 61710276Ssam } 61810276Ssam ; 61910276Ssam 62010276Ssam pathstring: STRING 62110276Ssam ; 62210276Ssam 62336933Skarels octal_number: NUMBER 62436933Skarels = { 62536933Skarels register int ret, dec, multby, digit; 62636933Skarels 62736933Skarels /* 62836933Skarels * Convert a number that was read as decimal number 62936933Skarels * to what it would be if it had been read as octal. 63036933Skarels */ 63136933Skarels dec = $1; 63236933Skarels multby = 1; 63336933Skarels ret = 0; 63436933Skarels while (dec) { 63536933Skarels digit = dec%10; 63636933Skarels if (digit > 7) { 63736933Skarels ret = -1; 63836933Skarels break; 63936933Skarels } 64036933Skarels ret += digit * multby; 64136933Skarels multby *= 8; 64236933Skarels dec /= 10; 64336933Skarels } 64436933Skarels $$ = ret; 64536933Skarels } 64636933Skarels ; 64736933Skarels 64810276Ssam check_login: /* empty */ 64910276Ssam = { 65010276Ssam if (logged_in) 65110276Ssam $$ = 1; 65210276Ssam else { 65310276Ssam reply(530, "Please login with USER and PASS."); 65410276Ssam $$ = 0; 65510276Ssam } 65610276Ssam } 65710276Ssam ; 65810276Ssam 65910276Ssam %% 66010276Ssam 66110276Ssam extern jmp_buf errcatch; 66210276Ssam 66310276Ssam #define CMD 0 /* beginning of command */ 66410276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 66510276Ssam #define STR1 2 /* expect SP followed by STRING */ 66610276Ssam #define STR2 3 /* expect STRING */ 66736304Skarels #define OSTR 4 /* optional SP then STRING */ 66836304Skarels #define ZSTR1 5 /* SP then optional STRING */ 66936304Skarels #define ZSTR2 6 /* optional STRING after SP */ 67036933Skarels #define SITECMD 7 /* SITE command */ 67136933Skarels #define NSTR 8 /* Number followed by a string */ 67210276Ssam 67310276Ssam struct tab { 67410276Ssam char *name; 67510276Ssam short token; 67610276Ssam short state; 67710276Ssam short implemented; /* 1 if command is implemented */ 67810276Ssam char *help; 67910276Ssam }; 68010276Ssam 68110276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 68210276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 68336304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 68410276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 68536933Skarels { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 68610276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 68710276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 68810276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 68926045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 69010276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 69110276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 69210276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 69310276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 69410276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 69510276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 69610276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 69710276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 69810276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 69910276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 70010276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 70110276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 70210276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 70310276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 70438135Srick { "REST", REST, ARGS, 1, "(restart command)" }, 70510276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 70610276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 70726045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 70810276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 70936304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 71010276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 71110276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 71210276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 71336933Skarels { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 71436552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 71536933Skarels { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 71610276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 71710276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 71836620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 71936620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 72036620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 72136620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 72236620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 72336620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 72436620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 72536620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 72626045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 72736933Skarels { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 72836933Skarels { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 72910276Ssam { NULL, 0, 0, 0, 0 } 73010276Ssam }; 73110276Ssam 73236933Skarels struct tab sitetab[] = { 73336933Skarels { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 73436933Skarels { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 73536933Skarels { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 73636933Skarels { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 73736933Skarels { NULL, 0, 0, 0, 0 } 73836933Skarels }; 73936933Skarels 74010276Ssam struct tab * 74136933Skarels lookup(p, cmd) 74236933Skarels register struct tab *p; 74310276Ssam char *cmd; 74410276Ssam { 74510276Ssam 74636933Skarels for (; p->name != NULL; p++) 74710276Ssam if (strcmp(cmd, p->name) == 0) 74810276Ssam return (p); 74910276Ssam return (0); 75010276Ssam } 75110276Ssam 75213033Ssam #include <arpa/telnet.h> 75310276Ssam 75410276Ssam /* 75510276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 75610276Ssam */ 75710276Ssam char * 75810276Ssam getline(s, n, iop) 75910276Ssam char *s; 76010276Ssam register FILE *iop; 76110276Ssam { 76210276Ssam register c; 76326494Sminshall register char *cs; 76410276Ssam 76510276Ssam cs = s; 76627751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 76726045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 76826045Sminshall *cs++ = tmpline[c]; 76926045Sminshall if (tmpline[c] == '\n') { 77026045Sminshall *cs++ = '\0'; 77136304Skarels if (debug) 77236304Skarels syslog(LOG_DEBUG, "command: %s", s); 77326045Sminshall tmpline[0] = '\0'; 77426045Sminshall return(s); 77526045Sminshall } 77636304Skarels if (c == 0) 77726045Sminshall tmpline[0] = '\0'; 77826045Sminshall } 77936304Skarels while ((c = getc(iop)) != EOF) { 78036304Skarels c &= 0377; 78136304Skarels if (c == IAC) { 78236304Skarels if ((c = getc(iop)) != EOF) { 78336304Skarels c &= 0377; 78436304Skarels switch (c) { 78527751Sminshall case WILL: 78627751Sminshall case WONT: 78736277Sbostic c = getc(iop); 78836304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 78927751Sminshall (void) fflush(stdout); 79036304Skarels continue; 79127751Sminshall case DO: 79227751Sminshall case DONT: 79336277Sbostic c = getc(iop); 79436304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 79527751Sminshall (void) fflush(stdout); 79636304Skarels continue; 79736304Skarels case IAC: 79827751Sminshall break; 79927751Sminshall default: 80036304Skarels continue; /* ignore command */ 80127751Sminshall } 80236304Skarels } 80310276Ssam } 80436304Skarels *cs++ = c; 80536304Skarels if (--n <= 0 || c == '\n') 80610276Ssam break; 80710276Ssam } 80827751Sminshall if (c == EOF && cs == s) 80918303Sralph return (NULL); 81010276Ssam *cs++ = '\0'; 811*52999Sbostic if (debug) { 812*52999Sbostic if (!guest && strncasecmp("pass ", s, 5) == 0) { 813*52999Sbostic /* Don't syslog passwords */ 814*52999Sbostic syslog(LOG_DEBUG, "command: %.5s ???", s); 815*52999Sbostic } else { 816*52999Sbostic register char *cp; 817*52999Sbostic register int len; 818*52999Sbostic 819*52999Sbostic /* Don't syslog trailing CR-LF */ 820*52999Sbostic len = strlen(s); 821*52999Sbostic cp = s + len - 1; 822*52999Sbostic while (cp >= s && (*cp == '\n' || *cp == '\r')) { 823*52999Sbostic --cp; 824*52999Sbostic --len; 825*52999Sbostic } 826*52999Sbostic syslog(LOG_DEBUG, "command: %.*s", len, s); 827*52999Sbostic } 828*52999Sbostic } 82910276Ssam return (s); 83010276Ssam } 83110276Ssam 83246669Sbostic static void 83311652Ssam toolong() 83411652Ssam { 83511652Ssam 83611652Ssam reply(421, 837*52999Sbostic "Timeout (%d seconds): closing control connection.", timeout); 838*52999Sbostic if (logging) 839*52999Sbostic syslog(LOG_INFO, "User %s timed out after %d seconds", 840*52999Sbostic (pw ? pw -> pw_name : "unknown"), timeout); 84113246Ssam dologout(1); 84211652Ssam } 84311652Ssam 84410276Ssam yylex() 84510276Ssam { 84610276Ssam static int cpos, state; 84736933Skarels register char *cp, *cp2; 84810276Ssam register struct tab *p; 84910276Ssam int n; 85046669Sbostic char c, *copy(); 85110276Ssam 85210276Ssam for (;;) { 85310276Ssam switch (state) { 85410276Ssam 85510276Ssam case CMD: 85626494Sminshall (void) signal(SIGALRM, toolong); 85726494Sminshall (void) alarm((unsigned) timeout); 85810276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 85910276Ssam reply(221, "You could at least say goodbye."); 86013246Ssam dologout(0); 86110276Ssam } 86226494Sminshall (void) alarm(0); 86336620Srick #ifdef SETPROCTITLE 86436933Skarels if (strncasecmp(cbuf, "PASS", 4) != NULL) 86536933Skarels setproctitle("%s: %s", proctitle, cbuf); 86636620Srick #endif /* SETPROCTITLE */ 86736324Sbostic if ((cp = index(cbuf, '\r'))) { 86836324Sbostic *cp++ = '\n'; 86936324Sbostic *cp = '\0'; 87036324Sbostic } 87136277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 87236277Sbostic cpos = cp - cbuf; 87336304Skarels if (cpos == 0) 87410276Ssam cpos = 4; 87510276Ssam c = cbuf[cpos]; 87610276Ssam cbuf[cpos] = '\0'; 87710276Ssam upper(cbuf); 87836933Skarels p = lookup(cmdtab, cbuf); 87910276Ssam cbuf[cpos] = c; 88010276Ssam if (p != 0) { 88110276Ssam if (p->implemented == 0) { 88210276Ssam nack(p->name); 88326494Sminshall longjmp(errcatch,0); 88410276Ssam /* NOTREACHED */ 88510276Ssam } 88610276Ssam state = p->state; 88736933Skarels *(char **)&yylval = p->name; 88810276Ssam return (p->token); 88910276Ssam } 89010276Ssam break; 89110276Ssam 89236933Skarels case SITECMD: 89336933Skarels if (cbuf[cpos] == ' ') { 89436933Skarels cpos++; 89536933Skarels return (SP); 89636933Skarels } 89736933Skarels cp = &cbuf[cpos]; 89836933Skarels if ((cp2 = strpbrk(cp, " \n"))) 89936933Skarels cpos = cp2 - cbuf; 90036933Skarels c = cbuf[cpos]; 90136933Skarels cbuf[cpos] = '\0'; 90236933Skarels upper(cp); 90336933Skarels p = lookup(sitetab, cp); 90436933Skarels cbuf[cpos] = c; 90536933Skarels if (p != 0) { 90636933Skarels if (p->implemented == 0) { 90736933Skarels state = CMD; 90836933Skarels nack(p->name); 90936933Skarels longjmp(errcatch,0); 91036933Skarels /* NOTREACHED */ 91136933Skarels } 91236933Skarels state = p->state; 91336933Skarels *(char **)&yylval = p->name; 91436933Skarels return (p->token); 91536933Skarels } 91636933Skarels state = CMD; 91736933Skarels break; 91836933Skarels 91910276Ssam case OSTR: 92010276Ssam if (cbuf[cpos] == '\n') { 92110276Ssam state = CMD; 92210276Ssam return (CRLF); 92310276Ssam } 92436317Sbostic /* FALLTHROUGH */ 92510276Ssam 92610276Ssam case STR1: 92736304Skarels case ZSTR1: 92836933Skarels dostr1: 92910276Ssam if (cbuf[cpos] == ' ') { 93010276Ssam cpos++; 93136317Sbostic state = state == OSTR ? STR2 : ++state; 93210276Ssam return (SP); 93310276Ssam } 93410276Ssam break; 93510276Ssam 93636304Skarels case ZSTR2: 93736304Skarels if (cbuf[cpos] == '\n') { 93836304Skarels state = CMD; 93936304Skarels return (CRLF); 94036304Skarels } 94136933Skarels /* FALLTHROUGH */ 94236304Skarels 94310276Ssam case STR2: 94410276Ssam cp = &cbuf[cpos]; 94510276Ssam n = strlen(cp); 94610276Ssam cpos += n - 1; 94710276Ssam /* 94810276Ssam * Make sure the string is nonempty and \n terminated. 94910276Ssam */ 95010276Ssam if (n > 1 && cbuf[cpos] == '\n') { 95110276Ssam cbuf[cpos] = '\0'; 95236933Skarels *(char **)&yylval = copy(cp); 95310276Ssam cbuf[cpos] = '\n'; 95410276Ssam state = ARGS; 95510276Ssam return (STRING); 95610276Ssam } 95710276Ssam break; 95810276Ssam 95936933Skarels case NSTR: 96036933Skarels if (cbuf[cpos] == ' ') { 96136933Skarels cpos++; 96236933Skarels return (SP); 96336933Skarels } 96436933Skarels if (isdigit(cbuf[cpos])) { 96536933Skarels cp = &cbuf[cpos]; 96636933Skarels while (isdigit(cbuf[++cpos])) 96736933Skarels ; 96836933Skarels c = cbuf[cpos]; 96936933Skarels cbuf[cpos] = '\0'; 97036933Skarels yylval = atoi(cp); 97136933Skarels cbuf[cpos] = c; 97236933Skarels state = STR1; 97336933Skarels return (NUMBER); 97436933Skarels } 97536933Skarels state = STR1; 97636933Skarels goto dostr1; 97736933Skarels 97810276Ssam case ARGS: 97910276Ssam if (isdigit(cbuf[cpos])) { 98010276Ssam cp = &cbuf[cpos]; 98110276Ssam while (isdigit(cbuf[++cpos])) 98210276Ssam ; 98310276Ssam c = cbuf[cpos]; 98410276Ssam cbuf[cpos] = '\0'; 98510276Ssam yylval = atoi(cp); 98610276Ssam cbuf[cpos] = c; 98710276Ssam return (NUMBER); 98810276Ssam } 98910276Ssam switch (cbuf[cpos++]) { 99010276Ssam 99110276Ssam case '\n': 99210276Ssam state = CMD; 99310276Ssam return (CRLF); 99410276Ssam 99510276Ssam case ' ': 99610276Ssam return (SP); 99710276Ssam 99810276Ssam case ',': 99910276Ssam return (COMMA); 100010276Ssam 100110276Ssam case 'A': 100210276Ssam case 'a': 100310276Ssam return (A); 100410276Ssam 100510276Ssam case 'B': 100610276Ssam case 'b': 100710276Ssam return (B); 100810276Ssam 100910276Ssam case 'C': 101010276Ssam case 'c': 101110276Ssam return (C); 101210276Ssam 101310276Ssam case 'E': 101410276Ssam case 'e': 101510276Ssam return (E); 101610276Ssam 101710276Ssam case 'F': 101810276Ssam case 'f': 101910276Ssam return (F); 102010276Ssam 102110276Ssam case 'I': 102210276Ssam case 'i': 102310276Ssam return (I); 102410276Ssam 102510276Ssam case 'L': 102610276Ssam case 'l': 102710276Ssam return (L); 102810276Ssam 102910276Ssam case 'N': 103010276Ssam case 'n': 103110276Ssam return (N); 103210276Ssam 103310276Ssam case 'P': 103410276Ssam case 'p': 103510276Ssam return (P); 103610276Ssam 103710276Ssam case 'R': 103810276Ssam case 'r': 103910276Ssam return (R); 104010276Ssam 104110276Ssam case 'S': 104210276Ssam case 's': 104310276Ssam return (S); 104410276Ssam 104510276Ssam case 'T': 104610276Ssam case 't': 104710276Ssam return (T); 104810276Ssam 104910276Ssam } 105010276Ssam break; 105110276Ssam 105210276Ssam default: 105310276Ssam fatal("Unknown state in scanner."); 105410276Ssam } 105526494Sminshall yyerror((char *) 0); 105610276Ssam state = CMD; 105726494Sminshall longjmp(errcatch,0); 105810276Ssam } 105910276Ssam } 106010276Ssam 106110276Ssam upper(s) 106236277Sbostic register char *s; 106310276Ssam { 106410276Ssam while (*s != '\0') { 106510276Ssam if (islower(*s)) 106610276Ssam *s = toupper(*s); 106710276Ssam s++; 106810276Ssam } 106910276Ssam } 107010276Ssam 107136933Skarels char * 107210276Ssam copy(s) 107310276Ssam char *s; 107410276Ssam { 107510276Ssam char *p; 107610276Ssam 107726494Sminshall p = malloc((unsigned) strlen(s) + 1); 107810276Ssam if (p == NULL) 107910276Ssam fatal("Ran out of memory."); 108026494Sminshall (void) strcpy(p, s); 108136933Skarels return (p); 108210276Ssam } 108310276Ssam 108436933Skarels help(ctab, s) 108536933Skarels struct tab *ctab; 108610276Ssam char *s; 108710276Ssam { 108810276Ssam register struct tab *c; 108910276Ssam register int width, NCMDS; 109036933Skarels char *type; 109110276Ssam 109236933Skarels if (ctab == sitetab) 109336933Skarels type = "SITE "; 109436933Skarels else 109536933Skarels type = ""; 109610276Ssam width = 0, NCMDS = 0; 109736933Skarels for (c = ctab; c->name != NULL; c++) { 109836933Skarels int len = strlen(c->name); 109910276Ssam 110010276Ssam if (len > width) 110110276Ssam width = len; 110210276Ssam NCMDS++; 110310276Ssam } 110410276Ssam width = (width + 8) &~ 7; 110510276Ssam if (s == 0) { 110610276Ssam register int i, j, w; 110710276Ssam int columns, lines; 110810276Ssam 110936933Skarels lreply(214, "The following %scommands are recognized %s.", 111036933Skarels type, "(* =>'s unimplemented)"); 111110276Ssam columns = 76 / width; 111210276Ssam if (columns == 0) 111310276Ssam columns = 1; 111410276Ssam lines = (NCMDS + columns - 1) / columns; 111510276Ssam for (i = 0; i < lines; i++) { 111627107Smckusick printf(" "); 111710276Ssam for (j = 0; j < columns; j++) { 111836933Skarels c = ctab + j * lines + i; 111910276Ssam printf("%s%c", c->name, 112010276Ssam c->implemented ? ' ' : '*'); 112136933Skarels if (c + lines >= &ctab[NCMDS]) 112210276Ssam break; 112331132Smckusick w = strlen(c->name) + 1; 112410276Ssam while (w < width) { 112510276Ssam putchar(' '); 112610276Ssam w++; 112710276Ssam } 112810276Ssam } 112910276Ssam printf("\r\n"); 113010276Ssam } 113126494Sminshall (void) fflush(stdout); 113210276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 113310276Ssam return; 113410276Ssam } 113510276Ssam upper(s); 113636933Skarels c = lookup(ctab, s); 113710276Ssam if (c == (struct tab *)0) { 113827107Smckusick reply(502, "Unknown command %s.", s); 113910276Ssam return; 114010276Ssam } 114110276Ssam if (c->implemented) 114236933Skarels reply(214, "Syntax: %s%s %s", type, c->name, c->help); 114310276Ssam else 114436933Skarels reply(214, "%s%-*s\t%s; unimplemented.", type, width, 114536933Skarels c->name, c->help); 114610276Ssam } 114736933Skarels 114836933Skarels sizecmd(filename) 114936933Skarels char *filename; 115036933Skarels { 115136933Skarels switch (type) { 115236933Skarels case TYPE_L: 115336933Skarels case TYPE_I: { 115436933Skarels struct stat stbuf; 115536933Skarels if (stat(filename, &stbuf) < 0 || 115636933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) 115736933Skarels reply(550, "%s: not a plain file.", filename); 115836933Skarels else 115936933Skarels reply(213, "%lu", stbuf.st_size); 116036933Skarels break;} 116136933Skarels case TYPE_A: { 116236933Skarels FILE *fin; 116338135Srick register int c; 116438135Srick register long count; 116536933Skarels struct stat stbuf; 116636933Skarels fin = fopen(filename, "r"); 116736933Skarels if (fin == NULL) { 116836933Skarels perror_reply(550, filename); 116936933Skarels return; 117036933Skarels } 117136933Skarels if (fstat(fileno(fin), &stbuf) < 0 || 117236933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) { 117336933Skarels reply(550, "%s: not a plain file.", filename); 117436933Skarels (void) fclose(fin); 117536933Skarels return; 117636933Skarels } 117736933Skarels 117836933Skarels count = 0; 117936933Skarels while((c=getc(fin)) != EOF) { 118036933Skarels if (c == '\n') /* will get expanded to \r\n */ 118136933Skarels count++; 118236933Skarels count++; 118336933Skarels } 118436933Skarels (void) fclose(fin); 118536933Skarels 118636933Skarels reply(213, "%ld", count); 118736933Skarels break;} 118836933Skarels default: 118936933Skarels reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 119036933Skarels } 119136933Skarels } 1192