110276Ssam /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 533738Sbostic * Redistribution and use in source and binary forms are permitted 634769Sbostic * provided that the above copyright notice and this paragraph are 734769Sbostic * duplicated in all such forms and that any documentation, 834769Sbostic * advertising materials, and other materials related to such 934769Sbostic * distribution and use acknowledge that the software was developed 1034769Sbostic * by the University of California, Berkeley. The name of the 1134769Sbostic * University may not be used to endorse or promote products derived 1234769Sbostic * from this software without specific prior written permission. 1334769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1536933Skarels * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1634769Sbostic * 17*40184Smckusick * @(#)ftpcmd.y 5.22 (Berkeley) 02/20/90 1822501Sdist */ 1922501Sdist 2022501Sdist /* 2110276Ssam * Grammar for FTP commands. 2236933Skarels * See RFC 959. 2310276Ssam */ 2410276Ssam 2510276Ssam %{ 2610276Ssam 2710276Ssam #ifndef lint 28*40184Smckusick static char sccsid[] = "@(#)ftpcmd.y 5.22 (Berkeley) 02/20/90"; 2933738Sbostic #endif /* not lint */ 3010276Ssam 3136552Sbostic #include <sys/param.h> 3210276Ssam #include <sys/socket.h> 3310276Ssam 3410276Ssam #include <netinet/in.h> 3510276Ssam 3613033Ssam #include <arpa/ftp.h> 3713033Ssam 3810276Ssam #include <stdio.h> 3911652Ssam #include <signal.h> 4010276Ssam #include <ctype.h> 4110276Ssam #include <pwd.h> 4210276Ssam #include <setjmp.h> 4326494Sminshall #include <syslog.h> 4436933Skarels #include <sys/stat.h> 4536933Skarels #include <time.h> 4610276Ssam 4710276Ssam extern struct sockaddr_in data_dest; 4810276Ssam extern int logged_in; 4910276Ssam extern struct passwd *pw; 5010276Ssam extern int guest; 5110276Ssam extern int logging; 5210276Ssam extern int type; 5310276Ssam extern int form; 5410276Ssam extern int debug; 5511652Ssam extern int timeout; 5636933Skarels extern int maxtimeout; 5726045Sminshall extern int pdata; 5836620Srick extern char hostname[], remotehost[]; 5936933Skarels extern char proctitle[]; 6010276Ssam extern char *globerr; 6110320Ssam extern int usedefault; 6226045Sminshall extern int transflag; 6326045Sminshall extern char tmpline[]; 6410276Ssam char **glob(); 6510276Ssam 6638135Srick off_t restart_point; 6738135Srick 6810276Ssam static int cmd_type; 6910276Ssam static int cmd_form; 7010276Ssam static int cmd_bytesz; 7136304Skarels char cbuf[512]; 7236304Skarels char *fromname; 7310276Ssam 7410276Ssam char *index(); 7510276Ssam %} 7610276Ssam 7710276Ssam %token 7810276Ssam A B C E F I 7910276Ssam L N P R S T 8010276Ssam 8110276Ssam SP CRLF COMMA STRING NUMBER 8210276Ssam 8310276Ssam USER PASS ACCT REIN QUIT PORT 8410276Ssam PASV TYPE STRU MODE RETR STOR 8510276Ssam APPE MLFL MAIL MSND MSOM MSAM 8610276Ssam MRSQ MRCP ALLO REST RNFR RNTO 8710276Ssam ABOR DELE CWD LIST NLST SITE 8836620Srick STAT HELP NOOP MKD RMD PWD 8936933Skarels CDUP STOU SMNT SYST SIZE MDTM 9010276Ssam 9136933Skarels UMASK IDLE CHMOD 9236933Skarels 9310276Ssam LEXERR 9410276Ssam 9510276Ssam %start cmd_list 9610276Ssam 9710276Ssam %% 9810276Ssam 9910276Ssam cmd_list: /* empty */ 10010276Ssam | cmd_list cmd 10130945Scsvsj = { 10230945Scsvsj fromname = (char *) 0; 10338135Srick restart_point = (off_t) 0; 10430945Scsvsj } 10530945Scsvsj | cmd_list rcmd 10610276Ssam ; 10710276Ssam 10810276Ssam cmd: USER SP username CRLF 10910276Ssam = { 11036304Skarels user((char *) $3); 11126494Sminshall free((char *) $3); 11210276Ssam } 11310276Ssam | PASS SP password CRLF 11410276Ssam = { 11526494Sminshall pass((char *) $3); 11626494Sminshall free((char *) $3); 11710276Ssam } 11810276Ssam | PORT SP host_port CRLF 11910276Ssam = { 12010320Ssam usedefault = 0; 12136304Skarels if (pdata >= 0) { 12226045Sminshall (void) close(pdata); 12336304Skarels pdata = -1; 12426045Sminshall } 12527107Smckusick reply(200, "PORT command successful."); 12610276Ssam } 12726045Sminshall | PASV CRLF 12826045Sminshall = { 12926045Sminshall passive(); 13026045Sminshall } 13110276Ssam | TYPE SP type_code CRLF 13210276Ssam = { 13310276Ssam switch (cmd_type) { 13410276Ssam 13510276Ssam case TYPE_A: 13610276Ssam if (cmd_form == FORM_N) { 13710276Ssam reply(200, "Type set to A."); 13810276Ssam type = cmd_type; 13910276Ssam form = cmd_form; 14010276Ssam } else 14110276Ssam reply(504, "Form must be N."); 14210276Ssam break; 14310276Ssam 14410276Ssam case TYPE_E: 14510276Ssam reply(504, "Type E not implemented."); 14610276Ssam break; 14710276Ssam 14810276Ssam case TYPE_I: 14910276Ssam reply(200, "Type set to I."); 15010276Ssam type = cmd_type; 15110276Ssam break; 15210276Ssam 15310276Ssam case TYPE_L: 15436933Skarels #if NBBY == 8 15536933Skarels if (cmd_bytesz == 8) { 15610276Ssam reply(200, 15736933Skarels "Type set to L (byte size 8)."); 15810276Ssam type = cmd_type; 15910276Ssam } else 16036933Skarels reply(504, "Byte size must be 8."); 16136933Skarels #else /* NBBY == 8 */ 16236933Skarels UNIMPLEMENTED for NBBY != 8 16336933Skarels #endif /* NBBY == 8 */ 16410276Ssam } 16510276Ssam } 16610276Ssam | STRU SP struct_code CRLF 16710276Ssam = { 16810276Ssam switch ($3) { 16910276Ssam 17010276Ssam case STRU_F: 17110276Ssam reply(200, "STRU F ok."); 17210276Ssam break; 17310276Ssam 17410276Ssam default: 17527107Smckusick reply(504, "Unimplemented STRU type."); 17610276Ssam } 17710276Ssam } 17810276Ssam | MODE SP mode_code CRLF 17910276Ssam = { 18010276Ssam switch ($3) { 18110276Ssam 18210276Ssam case MODE_S: 18310276Ssam reply(200, "MODE S ok."); 18410276Ssam break; 18510276Ssam 18610276Ssam default: 18710276Ssam reply(502, "Unimplemented MODE type."); 18810276Ssam } 18910276Ssam } 19010276Ssam | ALLO SP NUMBER CRLF 19110276Ssam = { 19227107Smckusick reply(202, "ALLO command ignored."); 19310276Ssam } 19436933Skarels | ALLO SP NUMBER SP R SP NUMBER CRLF 19536933Skarels = { 19636933Skarels reply(202, "ALLO command ignored."); 19736933Skarels } 19810276Ssam | RETR check_login SP pathname CRLF 19910276Ssam = { 20010302Ssam if ($2 && $4 != NULL) 20136933Skarels retrieve((char *) 0, (char *) $4); 20210302Ssam if ($4 != NULL) 20326494Sminshall free((char *) $4); 20410276Ssam } 20510276Ssam | STOR check_login SP pathname CRLF 20610276Ssam = { 20710302Ssam if ($2 && $4 != NULL) 20836304Skarels store((char *) $4, "w", 0); 20910302Ssam if ($4 != NULL) 21026494Sminshall free((char *) $4); 21110276Ssam } 21210276Ssam | APPE check_login SP pathname CRLF 21310276Ssam = { 21410302Ssam if ($2 && $4 != NULL) 21536304Skarels store((char *) $4, "a", 0); 21610302Ssam if ($4 != NULL) 21726494Sminshall free((char *) $4); 21810276Ssam } 21910276Ssam | NLST check_login CRLF 22010276Ssam = { 22110276Ssam if ($2) 22236620Srick send_file_list("."); 22310276Ssam } 22436620Srick | NLST check_login SP STRING CRLF 22510276Ssam = { 22636620Srick if ($2 && $4 != NULL) 22736620Srick send_file_list((char *) $4); 22810302Ssam if ($4 != NULL) 22926494Sminshall free((char *) $4); 23010276Ssam } 23110276Ssam | LIST check_login CRLF 23210276Ssam = { 23310276Ssam if ($2) 23436620Srick retrieve("/bin/ls -lgA", ""); 23510276Ssam } 23610276Ssam | LIST check_login SP pathname CRLF 23710276Ssam = { 23810302Ssam if ($2 && $4 != NULL) 23936620Srick retrieve("/bin/ls -lgA %s", (char *) $4); 24010302Ssam if ($4 != NULL) 24126494Sminshall free((char *) $4); 24210276Ssam } 24336933Skarels | STAT check_login SP pathname CRLF 24436933Skarels = { 24536933Skarels if ($2 && $4 != NULL) 24636933Skarels statfilecmd((char *) $4); 24736933Skarels if ($4 != NULL) 24836933Skarels free((char *) $4); 24936933Skarels } 25036933Skarels | STAT CRLF 25136933Skarels = { 25236933Skarels statcmd(); 25336933Skarels } 25410276Ssam | DELE check_login SP pathname CRLF 25510276Ssam = { 25610302Ssam if ($2 && $4 != NULL) 25726494Sminshall delete((char *) $4); 25810302Ssam if ($4 != NULL) 25926494Sminshall free((char *) $4); 26010276Ssam } 26130945Scsvsj | RNTO SP pathname CRLF 26230945Scsvsj = { 26330945Scsvsj if (fromname) { 26430945Scsvsj renamecmd(fromname, (char *) $3); 26530945Scsvsj free(fromname); 26630945Scsvsj fromname = (char *) 0; 26730945Scsvsj } else { 26830945Scsvsj reply(503, "Bad sequence of commands."); 26930945Scsvsj } 27030945Scsvsj free((char *) $3); 27130945Scsvsj } 27226045Sminshall | ABOR CRLF 27326045Sminshall = { 27427107Smckusick reply(225, "ABOR command successful."); 27526045Sminshall } 27610276Ssam | CWD check_login CRLF 27710276Ssam = { 27810276Ssam if ($2) 27910276Ssam cwd(pw->pw_dir); 28010276Ssam } 28110276Ssam | CWD check_login SP pathname CRLF 28210276Ssam = { 28310302Ssam if ($2 && $4 != NULL) 28426494Sminshall cwd((char *) $4); 28510302Ssam if ($4 != NULL) 28626494Sminshall free((char *) $4); 28710276Ssam } 28810276Ssam | HELP CRLF 28910276Ssam = { 29036933Skarels help(cmdtab, (char *) 0); 29110276Ssam } 29210276Ssam | HELP SP STRING CRLF 29310276Ssam = { 29436933Skarels register char *cp = (char *)$3; 29536933Skarels 29636933Skarels if (strncasecmp(cp, "SITE", 4) == 0) { 29736933Skarels cp = (char *)$3 + 4; 29836933Skarels if (*cp == ' ') 29936933Skarels cp++; 30036933Skarels if (*cp) 30136933Skarels help(sitetab, cp); 30236933Skarels else 30336933Skarels help(sitetab, (char *) 0); 30436933Skarels } else 30536933Skarels help(cmdtab, (char *) $3); 30610276Ssam } 30710276Ssam | NOOP CRLF 30810276Ssam = { 30927107Smckusick reply(200, "NOOP command successful."); 31010276Ssam } 31136620Srick | MKD check_login SP pathname CRLF 31210276Ssam = { 31310302Ssam if ($2 && $4 != NULL) 31426494Sminshall makedir((char *) $4); 31510302Ssam if ($4 != NULL) 31626494Sminshall free((char *) $4); 31710276Ssam } 31836620Srick | RMD check_login SP pathname CRLF 31910276Ssam = { 32010302Ssam if ($2 && $4 != NULL) 32126494Sminshall removedir((char *) $4); 32210302Ssam if ($4 != NULL) 32326494Sminshall free((char *) $4); 32410276Ssam } 32536620Srick | PWD check_login CRLF 32610276Ssam = { 32710276Ssam if ($2) 32810302Ssam pwd(); 32910276Ssam } 33036620Srick | CDUP check_login CRLF 33110276Ssam = { 33210276Ssam if ($2) 33310276Ssam cwd(".."); 33410276Ssam } 33536933Skarels | SITE SP HELP CRLF 33636933Skarels = { 33736933Skarels help(sitetab, (char *) 0); 33836933Skarels } 33936933Skarels | SITE SP HELP SP STRING CRLF 34036933Skarels = { 34136933Skarels help(sitetab, (char *) $5); 34236933Skarels } 34336933Skarels | SITE SP UMASK check_login CRLF 34436933Skarels = { 34536933Skarels int oldmask; 34636933Skarels 34736933Skarels if ($4) { 34836933Skarels oldmask = umask(0); 34936933Skarels (void) umask(oldmask); 35036933Skarels reply(200, "Current UMASK is %03o", oldmask); 35136933Skarels } 35236933Skarels } 35336933Skarels | SITE SP UMASK check_login SP octal_number CRLF 35436933Skarels = { 35536933Skarels int oldmask; 35636933Skarels 35736933Skarels if ($4) { 35836933Skarels if (($6 == -1) || ($6 > 0777)) { 35936933Skarels reply(501, "Bad UMASK value"); 36036933Skarels } else { 36136933Skarels oldmask = umask($6); 36236933Skarels reply(200, 36336933Skarels "UMASK set to %03o (was %03o)", 36436933Skarels $6, oldmask); 36536933Skarels } 36636933Skarels } 36736933Skarels } 36836933Skarels | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 36936933Skarels = { 37036933Skarels if ($4 && ($8 != NULL)) { 37136933Skarels if ($6 > 0777) 37236933Skarels reply(501, 37336933Skarels "CHMOD: Mode value must be between 0 and 0777"); 37436933Skarels else if (chmod((char *) $8, $6) < 0) 37536933Skarels perror_reply(550, (char *) $8); 37636933Skarels else 37736933Skarels reply(200, "CHMOD command successful."); 37836933Skarels } 37936933Skarels if ($8 != NULL) 38036933Skarels free((char *) $8); 38136933Skarels } 38236933Skarels | SITE SP IDLE CRLF 38336933Skarels = { 38436933Skarels reply(200, 38536933Skarels "Current IDLE time limit is %d seconds; max %d", 38636933Skarels timeout, maxtimeout); 38736933Skarels } 38836933Skarels | SITE SP IDLE SP NUMBER CRLF 38936933Skarels = { 39036933Skarels if ($5 < 30 || $5 > maxtimeout) { 39136933Skarels reply(501, 39236933Skarels "Maximum IDLE time must be between 30 and %d seconds", 39336933Skarels maxtimeout); 39436933Skarels } else { 39536933Skarels timeout = $5; 39636933Skarels (void) alarm((unsigned) timeout); 39736933Skarels reply(200, 39836933Skarels "Maximum IDLE time set to %d seconds", 39936933Skarels timeout); 40036933Skarels } 40136933Skarels } 40226045Sminshall | STOU check_login SP pathname CRLF 40326045Sminshall = { 40436304Skarels if ($2 && $4 != NULL) 40536304Skarels store((char *) $4, "w", 1); 40626045Sminshall if ($4 != NULL) 40726494Sminshall free((char *) $4); 40826045Sminshall } 40936552Sbostic | SYST CRLF 41036552Sbostic = { 41136640Srick #ifdef unix 41236933Skarels #ifdef BSD 41336552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 41436552Sbostic NBBY, BSD); 41536933Skarels #else /* BSD */ 41636933Skarels reply(215, "UNIX Type: L%d", NBBY); 41736933Skarels #endif /* BSD */ 41836933Skarels #else /* unix */ 41936933Skarels reply(215, "UNKNOWN Type: L%d", NBBY); 42036933Skarels #endif /* unix */ 42136552Sbostic } 42236933Skarels 42336933Skarels /* 42436933Skarels * SIZE is not in RFC959, but Postel has blessed it and 42536933Skarels * it will be in the updated RFC. 42636933Skarels * 42736933Skarels * Return size of file in a format suitable for 42836933Skarels * using with RESTART (we just count bytes). 42936933Skarels */ 43036933Skarels | SIZE check_login SP pathname CRLF 43136933Skarels = { 43236933Skarels if ($2 && $4 != NULL) 43336933Skarels sizecmd((char *) $4); 43436933Skarels if ($4 != NULL) 43536933Skarels free((char *) $4); 43636933Skarels } 43736933Skarels 43836933Skarels /* 43936933Skarels * MDTM is not in RFC959, but Postel has blessed it and 44036933Skarels * it will be in the updated RFC. 44136933Skarels * 44236933Skarels * Return modification time of file as an ISO 3307 44336933Skarels * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 44436933Skarels * where xxx is the fractional second (of any precision, 44536933Skarels * not necessarily 3 digits) 44636933Skarels */ 44736933Skarels | MDTM check_login SP pathname CRLF 44836933Skarels = { 44936933Skarels if ($2 && $4 != NULL) { 45036933Skarels struct stat stbuf; 45136933Skarels if (stat((char *) $4, &stbuf) < 0) 45236933Skarels perror_reply(550, "%s", (char *) $4); 45336933Skarels else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 45436933Skarels reply(550, "%s: not a plain file.", 45536933Skarels (char *) $4); 45636933Skarels } else { 45736933Skarels register struct tm *t; 45836933Skarels struct tm *gmtime(); 45936933Skarels t = gmtime(&stbuf.st_mtime); 46036933Skarels reply(213, 46136933Skarels "19%02d%02d%02d%02d%02d%02d", 46236933Skarels t->tm_year, t->tm_mon+1, t->tm_mday, 46336933Skarels t->tm_hour, t->tm_min, t->tm_sec); 46436933Skarels } 46536933Skarels } 46636933Skarels if ($4 != NULL) 46736933Skarels free((char *) $4); 46836933Skarels } 46910276Ssam | QUIT CRLF 47010276Ssam = { 47110276Ssam reply(221, "Goodbye."); 47213246Ssam dologout(0); 47310276Ssam } 47410276Ssam | error CRLF 47510276Ssam = { 47610276Ssam yyerrok; 47710276Ssam } 47810276Ssam ; 47930945Scsvsj rcmd: RNFR check_login SP pathname CRLF 48030945Scsvsj = { 48130945Scsvsj char *renamefrom(); 48230945Scsvsj 48338135Srick restart_point = (off_t) 0; 48430945Scsvsj if ($2 && $4) { 48530945Scsvsj fromname = renamefrom((char *) $4); 48630945Scsvsj if (fromname == (char *) 0 && $4) { 48730945Scsvsj free((char *) $4); 48830945Scsvsj } 48930945Scsvsj } 49030945Scsvsj } 49138135Srick | REST SP byte_size CRLF 49238135Srick = { 49338135Srick long atol(); 49438135Srick 49538135Srick fromname = (char *) 0; 49638135Srick restart_point = $3; 49738135Srick reply(350, "Restarting at %ld. %s", restart_point, 49838135Srick "Send STORE or RETRIEVE to initiate transfer."); 49938135Srick } 50030945Scsvsj ; 50130945Scsvsj 50210276Ssam username: STRING 50310276Ssam ; 50410276Ssam 50536304Skarels password: /* empty */ 50636304Skarels = { 507*40184Smckusick *(char **)&($$) = (char *)calloc(1, sizeof(char)); 50836304Skarels } 50936304Skarels | STRING 51010276Ssam ; 51110276Ssam 51210276Ssam byte_size: NUMBER 51310276Ssam ; 51410276Ssam 51510276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 51610276Ssam NUMBER COMMA NUMBER 51710276Ssam = { 51810276Ssam register char *a, *p; 51910276Ssam 52010276Ssam a = (char *)&data_dest.sin_addr; 52110276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 52210276Ssam p = (char *)&data_dest.sin_port; 52310276Ssam p[0] = $9; p[1] = $11; 52410324Ssam data_dest.sin_family = AF_INET; 52510276Ssam } 52610276Ssam ; 52710276Ssam 52810276Ssam form_code: N 52910276Ssam = { 53010276Ssam $$ = FORM_N; 53110276Ssam } 53210276Ssam | T 53310276Ssam = { 53410276Ssam $$ = FORM_T; 53510276Ssam } 53610276Ssam | C 53710276Ssam = { 53810276Ssam $$ = FORM_C; 53910276Ssam } 54010276Ssam ; 54110276Ssam 54210276Ssam type_code: A 54310276Ssam = { 54410276Ssam cmd_type = TYPE_A; 54510276Ssam cmd_form = FORM_N; 54610276Ssam } 54710276Ssam | A SP form_code 54810276Ssam = { 54910276Ssam cmd_type = TYPE_A; 55010276Ssam cmd_form = $3; 55110276Ssam } 55210276Ssam | E 55310276Ssam = { 55410276Ssam cmd_type = TYPE_E; 55510276Ssam cmd_form = FORM_N; 55610276Ssam } 55710276Ssam | E SP form_code 55810276Ssam = { 55910276Ssam cmd_type = TYPE_E; 56010276Ssam cmd_form = $3; 56110276Ssam } 56210276Ssam | I 56310276Ssam = { 56410276Ssam cmd_type = TYPE_I; 56510276Ssam } 56610276Ssam | L 56710276Ssam = { 56810276Ssam cmd_type = TYPE_L; 56936552Sbostic cmd_bytesz = NBBY; 57010276Ssam } 57110276Ssam | L SP byte_size 57210276Ssam = { 57310276Ssam cmd_type = TYPE_L; 57410276Ssam cmd_bytesz = $3; 57510276Ssam } 57610276Ssam /* this is for a bug in the BBN ftp */ 57710276Ssam | L byte_size 57810276Ssam = { 57910276Ssam cmd_type = TYPE_L; 58010276Ssam cmd_bytesz = $2; 58110276Ssam } 58210276Ssam ; 58310276Ssam 58410276Ssam struct_code: F 58510276Ssam = { 58610276Ssam $$ = STRU_F; 58710276Ssam } 58810276Ssam | R 58910276Ssam = { 59010276Ssam $$ = STRU_R; 59110276Ssam } 59210276Ssam | P 59310276Ssam = { 59410276Ssam $$ = STRU_P; 59510276Ssam } 59610276Ssam ; 59710276Ssam 59810276Ssam mode_code: S 59910276Ssam = { 60010276Ssam $$ = MODE_S; 60110276Ssam } 60210276Ssam | B 60310276Ssam = { 60410276Ssam $$ = MODE_B; 60510276Ssam } 60610276Ssam | C 60710276Ssam = { 60810276Ssam $$ = MODE_C; 60910276Ssam } 61010276Ssam ; 61110276Ssam 61210276Ssam pathname: pathstring 61310276Ssam = { 61427107Smckusick /* 61527107Smckusick * Problem: this production is used for all pathname 61627107Smckusick * processing, but only gives a 550 error reply. 61727107Smckusick * This is a valid reply in some cases but not in others. 61827107Smckusick */ 61936304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 62036933Skarels *(char **)&($$) = *glob((char *) $1); 62110302Ssam if (globerr != NULL) { 62210276Ssam reply(550, globerr); 62310302Ssam $$ = NULL; 62410302Ssam } 62526494Sminshall free((char *) $1); 62610276Ssam } else 62710276Ssam $$ = $1; 62810276Ssam } 62910276Ssam ; 63010276Ssam 63110276Ssam pathstring: STRING 63210276Ssam ; 63310276Ssam 63436933Skarels octal_number: NUMBER 63536933Skarels = { 63636933Skarels register int ret, dec, multby, digit; 63736933Skarels 63836933Skarels /* 63936933Skarels * Convert a number that was read as decimal number 64036933Skarels * to what it would be if it had been read as octal. 64136933Skarels */ 64236933Skarels dec = $1; 64336933Skarels multby = 1; 64436933Skarels ret = 0; 64536933Skarels while (dec) { 64636933Skarels digit = dec%10; 64736933Skarels if (digit > 7) { 64836933Skarels ret = -1; 64936933Skarels break; 65036933Skarels } 65136933Skarels ret += digit * multby; 65236933Skarels multby *= 8; 65336933Skarels dec /= 10; 65436933Skarels } 65536933Skarels $$ = ret; 65636933Skarels } 65736933Skarels ; 65836933Skarels 65910276Ssam check_login: /* empty */ 66010276Ssam = { 66110276Ssam if (logged_in) 66210276Ssam $$ = 1; 66310276Ssam else { 66410276Ssam reply(530, "Please login with USER and PASS."); 66510276Ssam $$ = 0; 66610276Ssam } 66710276Ssam } 66810276Ssam ; 66910276Ssam 67010276Ssam %% 67110276Ssam 67210276Ssam extern jmp_buf errcatch; 67310276Ssam 67410276Ssam #define CMD 0 /* beginning of command */ 67510276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 67610276Ssam #define STR1 2 /* expect SP followed by STRING */ 67710276Ssam #define STR2 3 /* expect STRING */ 67836304Skarels #define OSTR 4 /* optional SP then STRING */ 67936304Skarels #define ZSTR1 5 /* SP then optional STRING */ 68036304Skarels #define ZSTR2 6 /* optional STRING after SP */ 68136933Skarels #define SITECMD 7 /* SITE command */ 68236933Skarels #define NSTR 8 /* Number followed by a string */ 68310276Ssam 68410276Ssam struct tab { 68510276Ssam char *name; 68610276Ssam short token; 68710276Ssam short state; 68810276Ssam short implemented; /* 1 if command is implemented */ 68910276Ssam char *help; 69010276Ssam }; 69110276Ssam 69210276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 69310276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 69436304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 69510276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 69636933Skarels { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 69710276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 69810276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 69910276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 70026045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 70110276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 70210276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 70310276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 70410276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 70510276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 70610276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 70710276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 70810276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 70910276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 71010276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 71110276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 71210276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 71310276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 71410276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 71538135Srick { "REST", REST, ARGS, 1, "(restart command)" }, 71610276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 71710276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 71826045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 71910276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 72036304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 72110276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 72210276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 72310276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 72436933Skarels { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 72536552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 72636933Skarels { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 72710276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 72810276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 72936620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 73036620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 73136620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 73236620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 73336620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 73436620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 73536620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 73636620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 73726045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 73836933Skarels { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 73936933Skarels { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 74010276Ssam { NULL, 0, 0, 0, 0 } 74110276Ssam }; 74210276Ssam 74336933Skarels struct tab sitetab[] = { 74436933Skarels { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 74536933Skarels { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 74636933Skarels { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 74736933Skarels { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 74836933Skarels { NULL, 0, 0, 0, 0 } 74936933Skarels }; 75036933Skarels 75110276Ssam struct tab * 75236933Skarels lookup(p, cmd) 75336933Skarels register struct tab *p; 75410276Ssam char *cmd; 75510276Ssam { 75610276Ssam 75736933Skarels for (; p->name != NULL; p++) 75810276Ssam if (strcmp(cmd, p->name) == 0) 75910276Ssam return (p); 76010276Ssam return (0); 76110276Ssam } 76210276Ssam 76313033Ssam #include <arpa/telnet.h> 76410276Ssam 76510276Ssam /* 76610276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 76710276Ssam */ 76810276Ssam char * 76910276Ssam getline(s, n, iop) 77010276Ssam char *s; 77110276Ssam register FILE *iop; 77210276Ssam { 77310276Ssam register c; 77426494Sminshall register char *cs; 77510276Ssam 77610276Ssam cs = s; 77727751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 77826045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 77926045Sminshall *cs++ = tmpline[c]; 78026045Sminshall if (tmpline[c] == '\n') { 78126045Sminshall *cs++ = '\0'; 78236304Skarels if (debug) 78336304Skarels syslog(LOG_DEBUG, "command: %s", s); 78426045Sminshall tmpline[0] = '\0'; 78526045Sminshall return(s); 78626045Sminshall } 78736304Skarels if (c == 0) 78826045Sminshall tmpline[0] = '\0'; 78926045Sminshall } 79036304Skarels while ((c = getc(iop)) != EOF) { 79136304Skarels c &= 0377; 79236304Skarels if (c == IAC) { 79336304Skarels if ((c = getc(iop)) != EOF) { 79436304Skarels c &= 0377; 79536304Skarels switch (c) { 79627751Sminshall case WILL: 79727751Sminshall case WONT: 79836277Sbostic c = getc(iop); 79936304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 80027751Sminshall (void) fflush(stdout); 80136304Skarels continue; 80227751Sminshall case DO: 80327751Sminshall case DONT: 80436277Sbostic c = getc(iop); 80536304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 80627751Sminshall (void) fflush(stdout); 80736304Skarels continue; 80836304Skarels case IAC: 80927751Sminshall break; 81027751Sminshall default: 81136304Skarels continue; /* ignore command */ 81227751Sminshall } 81336304Skarels } 81410276Ssam } 81536304Skarels *cs++ = c; 81636304Skarels if (--n <= 0 || c == '\n') 81710276Ssam break; 81810276Ssam } 81927751Sminshall if (c == EOF && cs == s) 82018303Sralph return (NULL); 82110276Ssam *cs++ = '\0'; 82236304Skarels if (debug) 82336304Skarels syslog(LOG_DEBUG, "command: %s", s); 82410276Ssam return (s); 82510276Ssam } 82610276Ssam 82711652Ssam static int 82811652Ssam toolong() 82911652Ssam { 83026494Sminshall time_t now; 83111652Ssam extern char *ctime(); 83226494Sminshall extern time_t time(); 83311652Ssam 83411652Ssam reply(421, 83511652Ssam "Timeout (%d seconds): closing control connection.", timeout); 83626494Sminshall (void) time(&now); 83711652Ssam if (logging) { 83826494Sminshall syslog(LOG_INFO, 83936304Skarels "User %s timed out after %d seconds at %s", 84011652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 84111652Ssam } 84213246Ssam dologout(1); 84311652Ssam } 84411652Ssam 84510276Ssam yylex() 84610276Ssam { 84710276Ssam static int cpos, state; 84836933Skarels register char *cp, *cp2; 84910276Ssam register struct tab *p; 85010276Ssam int n; 85136277Sbostic char c, *strpbrk(); 85236933Skarels char *copy(); 85310276Ssam 85410276Ssam for (;;) { 85510276Ssam switch (state) { 85610276Ssam 85710276Ssam case CMD: 85826494Sminshall (void) signal(SIGALRM, toolong); 85926494Sminshall (void) alarm((unsigned) timeout); 86010276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 86110276Ssam reply(221, "You could at least say goodbye."); 86213246Ssam dologout(0); 86310276Ssam } 86426494Sminshall (void) alarm(0); 86536620Srick #ifdef SETPROCTITLE 86636933Skarels if (strncasecmp(cbuf, "PASS", 4) != NULL) 86736933Skarels setproctitle("%s: %s", proctitle, cbuf); 86836620Srick #endif /* SETPROCTITLE */ 86936324Sbostic if ((cp = index(cbuf, '\r'))) { 87036324Sbostic *cp++ = '\n'; 87136324Sbostic *cp = '\0'; 87236324Sbostic } 87336277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 87436277Sbostic cpos = cp - cbuf; 87536304Skarels if (cpos == 0) 87610276Ssam cpos = 4; 87710276Ssam c = cbuf[cpos]; 87810276Ssam cbuf[cpos] = '\0'; 87910276Ssam upper(cbuf); 88036933Skarels p = lookup(cmdtab, cbuf); 88110276Ssam cbuf[cpos] = c; 88210276Ssam if (p != 0) { 88310276Ssam if (p->implemented == 0) { 88410276Ssam nack(p->name); 88526494Sminshall longjmp(errcatch,0); 88610276Ssam /* NOTREACHED */ 88710276Ssam } 88810276Ssam state = p->state; 88936933Skarels *(char **)&yylval = p->name; 89010276Ssam return (p->token); 89110276Ssam } 89210276Ssam break; 89310276Ssam 89436933Skarels case SITECMD: 89536933Skarels if (cbuf[cpos] == ' ') { 89636933Skarels cpos++; 89736933Skarels return (SP); 89836933Skarels } 89936933Skarels cp = &cbuf[cpos]; 90036933Skarels if ((cp2 = strpbrk(cp, " \n"))) 90136933Skarels cpos = cp2 - cbuf; 90236933Skarels c = cbuf[cpos]; 90336933Skarels cbuf[cpos] = '\0'; 90436933Skarels upper(cp); 90536933Skarels p = lookup(sitetab, cp); 90636933Skarels cbuf[cpos] = c; 90736933Skarels if (p != 0) { 90836933Skarels if (p->implemented == 0) { 90936933Skarels state = CMD; 91036933Skarels nack(p->name); 91136933Skarels longjmp(errcatch,0); 91236933Skarels /* NOTREACHED */ 91336933Skarels } 91436933Skarels state = p->state; 91536933Skarels *(char **)&yylval = p->name; 91636933Skarels return (p->token); 91736933Skarels } 91836933Skarels state = CMD; 91936933Skarels break; 92036933Skarels 92110276Ssam case OSTR: 92210276Ssam if (cbuf[cpos] == '\n') { 92310276Ssam state = CMD; 92410276Ssam return (CRLF); 92510276Ssam } 92636317Sbostic /* FALLTHROUGH */ 92710276Ssam 92810276Ssam case STR1: 92936304Skarels case ZSTR1: 93036933Skarels dostr1: 93110276Ssam if (cbuf[cpos] == ' ') { 93210276Ssam cpos++; 93336317Sbostic state = state == OSTR ? STR2 : ++state; 93410276Ssam return (SP); 93510276Ssam } 93610276Ssam break; 93710276Ssam 93836304Skarels case ZSTR2: 93936304Skarels if (cbuf[cpos] == '\n') { 94036304Skarels state = CMD; 94136304Skarels return (CRLF); 94236304Skarels } 94336933Skarels /* FALLTHROUGH */ 94436304Skarels 94510276Ssam case STR2: 94610276Ssam cp = &cbuf[cpos]; 94710276Ssam n = strlen(cp); 94810276Ssam cpos += n - 1; 94910276Ssam /* 95010276Ssam * Make sure the string is nonempty and \n terminated. 95110276Ssam */ 95210276Ssam if (n > 1 && cbuf[cpos] == '\n') { 95310276Ssam cbuf[cpos] = '\0'; 95436933Skarels *(char **)&yylval = copy(cp); 95510276Ssam cbuf[cpos] = '\n'; 95610276Ssam state = ARGS; 95710276Ssam return (STRING); 95810276Ssam } 95910276Ssam break; 96010276Ssam 96136933Skarels case NSTR: 96236933Skarels if (cbuf[cpos] == ' ') { 96336933Skarels cpos++; 96436933Skarels return (SP); 96536933Skarels } 96636933Skarels if (isdigit(cbuf[cpos])) { 96736933Skarels cp = &cbuf[cpos]; 96836933Skarels while (isdigit(cbuf[++cpos])) 96936933Skarels ; 97036933Skarels c = cbuf[cpos]; 97136933Skarels cbuf[cpos] = '\0'; 97236933Skarels yylval = atoi(cp); 97336933Skarels cbuf[cpos] = c; 97436933Skarels state = STR1; 97536933Skarels return (NUMBER); 97636933Skarels } 97736933Skarels state = STR1; 97836933Skarels goto dostr1; 97936933Skarels 98010276Ssam case ARGS: 98110276Ssam if (isdigit(cbuf[cpos])) { 98210276Ssam cp = &cbuf[cpos]; 98310276Ssam while (isdigit(cbuf[++cpos])) 98410276Ssam ; 98510276Ssam c = cbuf[cpos]; 98610276Ssam cbuf[cpos] = '\0'; 98710276Ssam yylval = atoi(cp); 98810276Ssam cbuf[cpos] = c; 98910276Ssam return (NUMBER); 99010276Ssam } 99110276Ssam switch (cbuf[cpos++]) { 99210276Ssam 99310276Ssam case '\n': 99410276Ssam state = CMD; 99510276Ssam return (CRLF); 99610276Ssam 99710276Ssam case ' ': 99810276Ssam return (SP); 99910276Ssam 100010276Ssam case ',': 100110276Ssam return (COMMA); 100210276Ssam 100310276Ssam case 'A': 100410276Ssam case 'a': 100510276Ssam return (A); 100610276Ssam 100710276Ssam case 'B': 100810276Ssam case 'b': 100910276Ssam return (B); 101010276Ssam 101110276Ssam case 'C': 101210276Ssam case 'c': 101310276Ssam return (C); 101410276Ssam 101510276Ssam case 'E': 101610276Ssam case 'e': 101710276Ssam return (E); 101810276Ssam 101910276Ssam case 'F': 102010276Ssam case 'f': 102110276Ssam return (F); 102210276Ssam 102310276Ssam case 'I': 102410276Ssam case 'i': 102510276Ssam return (I); 102610276Ssam 102710276Ssam case 'L': 102810276Ssam case 'l': 102910276Ssam return (L); 103010276Ssam 103110276Ssam case 'N': 103210276Ssam case 'n': 103310276Ssam return (N); 103410276Ssam 103510276Ssam case 'P': 103610276Ssam case 'p': 103710276Ssam return (P); 103810276Ssam 103910276Ssam case 'R': 104010276Ssam case 'r': 104110276Ssam return (R); 104210276Ssam 104310276Ssam case 'S': 104410276Ssam case 's': 104510276Ssam return (S); 104610276Ssam 104710276Ssam case 'T': 104810276Ssam case 't': 104910276Ssam return (T); 105010276Ssam 105110276Ssam } 105210276Ssam break; 105310276Ssam 105410276Ssam default: 105510276Ssam fatal("Unknown state in scanner."); 105610276Ssam } 105726494Sminshall yyerror((char *) 0); 105810276Ssam state = CMD; 105926494Sminshall longjmp(errcatch,0); 106010276Ssam } 106110276Ssam } 106210276Ssam 106310276Ssam upper(s) 106436277Sbostic register char *s; 106510276Ssam { 106610276Ssam while (*s != '\0') { 106710276Ssam if (islower(*s)) 106810276Ssam *s = toupper(*s); 106910276Ssam s++; 107010276Ssam } 107110276Ssam } 107210276Ssam 107336933Skarels char * 107410276Ssam copy(s) 107510276Ssam char *s; 107610276Ssam { 107710276Ssam char *p; 107826494Sminshall extern char *malloc(), *strcpy(); 107910276Ssam 108026494Sminshall p = malloc((unsigned) strlen(s) + 1); 108110276Ssam if (p == NULL) 108210276Ssam fatal("Ran out of memory."); 108326494Sminshall (void) strcpy(p, s); 108436933Skarels return (p); 108510276Ssam } 108610276Ssam 108736933Skarels help(ctab, s) 108836933Skarels struct tab *ctab; 108910276Ssam char *s; 109010276Ssam { 109110276Ssam register struct tab *c; 109210276Ssam register int width, NCMDS; 109336933Skarels char *type; 109410276Ssam 109536933Skarels if (ctab == sitetab) 109636933Skarels type = "SITE "; 109736933Skarels else 109836933Skarels type = ""; 109910276Ssam width = 0, NCMDS = 0; 110036933Skarels for (c = ctab; c->name != NULL; c++) { 110136933Skarels int len = strlen(c->name); 110210276Ssam 110310276Ssam if (len > width) 110410276Ssam width = len; 110510276Ssam NCMDS++; 110610276Ssam } 110710276Ssam width = (width + 8) &~ 7; 110810276Ssam if (s == 0) { 110910276Ssam register int i, j, w; 111010276Ssam int columns, lines; 111110276Ssam 111236933Skarels lreply(214, "The following %scommands are recognized %s.", 111336933Skarels type, "(* =>'s unimplemented)"); 111410276Ssam columns = 76 / width; 111510276Ssam if (columns == 0) 111610276Ssam columns = 1; 111710276Ssam lines = (NCMDS + columns - 1) / columns; 111810276Ssam for (i = 0; i < lines; i++) { 111927107Smckusick printf(" "); 112010276Ssam for (j = 0; j < columns; j++) { 112136933Skarels c = ctab + j * lines + i; 112210276Ssam printf("%s%c", c->name, 112310276Ssam c->implemented ? ' ' : '*'); 112436933Skarels if (c + lines >= &ctab[NCMDS]) 112510276Ssam break; 112631132Smckusick w = strlen(c->name) + 1; 112710276Ssam while (w < width) { 112810276Ssam putchar(' '); 112910276Ssam w++; 113010276Ssam } 113110276Ssam } 113210276Ssam printf("\r\n"); 113310276Ssam } 113426494Sminshall (void) fflush(stdout); 113510276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 113610276Ssam return; 113710276Ssam } 113810276Ssam upper(s); 113936933Skarels c = lookup(ctab, s); 114010276Ssam if (c == (struct tab *)0) { 114127107Smckusick reply(502, "Unknown command %s.", s); 114210276Ssam return; 114310276Ssam } 114410276Ssam if (c->implemented) 114536933Skarels reply(214, "Syntax: %s%s %s", type, c->name, c->help); 114610276Ssam else 114736933Skarels reply(214, "%s%-*s\t%s; unimplemented.", type, width, 114836933Skarels c->name, c->help); 114910276Ssam } 115036933Skarels 115136933Skarels sizecmd(filename) 115236933Skarels char *filename; 115336933Skarels { 115436933Skarels switch (type) { 115536933Skarels case TYPE_L: 115636933Skarels case TYPE_I: { 115736933Skarels struct stat stbuf; 115836933Skarels if (stat(filename, &stbuf) < 0 || 115936933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) 116036933Skarels reply(550, "%s: not a plain file.", filename); 116136933Skarels else 116236933Skarels reply(213, "%lu", stbuf.st_size); 116336933Skarels break;} 116436933Skarels case TYPE_A: { 116536933Skarels FILE *fin; 116638135Srick register int c; 116738135Srick register long count; 116836933Skarels struct stat stbuf; 116936933Skarels fin = fopen(filename, "r"); 117036933Skarels if (fin == NULL) { 117136933Skarels perror_reply(550, filename); 117236933Skarels return; 117336933Skarels } 117436933Skarels if (fstat(fileno(fin), &stbuf) < 0 || 117536933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) { 117636933Skarels reply(550, "%s: not a plain file.", filename); 117736933Skarels (void) fclose(fin); 117836933Skarels return; 117936933Skarels } 118036933Skarels 118136933Skarels count = 0; 118236933Skarels while((c=getc(fin)) != EOF) { 118336933Skarels if (c == '\n') /* will get expanded to \r\n */ 118436933Skarels count++; 118536933Skarels count++; 118636933Skarels } 118736933Skarels (void) fclose(fin); 118836933Skarels 118936933Skarels reply(213, "%ld", count); 119036933Skarels break;} 119136933Skarels default: 119236933Skarels reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 119336933Skarels } 119436933Skarels } 1195