110276Ssam /* 2*26045Sminshall * Copyright (c) 1985 Regents of the University of California. 322501Sdist * All rights reserved. The Berkeley software License Agreement 422501Sdist * specifies the terms and conditions for redistribution. 522501Sdist */ 622501Sdist 722501Sdist /* 810276Ssam * Grammar for FTP commands. 910276Ssam * See RFC 765. 1010276Ssam */ 1110276Ssam 1210276Ssam %{ 1310276Ssam 1410276Ssam #ifndef lint 15*26045Sminshall static char sccsid[] = "@(#)ftpcmd.y 5.3 (Berkeley) 02/03/86"; 1610276Ssam #endif 1710276Ssam 1810276Ssam #include <sys/types.h> 1910276Ssam #include <sys/socket.h> 2010276Ssam 2110276Ssam #include <netinet/in.h> 2210276Ssam 2313033Ssam #include <arpa/ftp.h> 2413033Ssam 2510276Ssam #include <stdio.h> 2611652Ssam #include <signal.h> 2710276Ssam #include <ctype.h> 2810276Ssam #include <pwd.h> 2910276Ssam #include <setjmp.h> 3010276Ssam 3110276Ssam extern struct sockaddr_in data_dest; 3210276Ssam extern int logged_in; 3310276Ssam extern struct passwd *pw; 3410276Ssam extern int guest; 3510276Ssam extern int logging; 3610276Ssam extern int type; 3710276Ssam extern int form; 3810276Ssam extern int debug; 3911652Ssam extern int timeout; 40*26045Sminshall extern int pdata; 4110276Ssam extern char hostname[]; 4210276Ssam extern char *globerr; 4310320Ssam extern int usedefault; 44*26045Sminshall extern int unique; 45*26045Sminshall extern int transflag; 46*26045Sminshall extern char tmpline[]; 4710276Ssam char **glob(); 4810276Ssam 4910276Ssam static int cmd_type; 5010276Ssam static int cmd_form; 5110276Ssam static int cmd_bytesz; 52*26045Sminshall char cbuf[512]; 5310276Ssam 5410276Ssam char *index(); 5510276Ssam %} 5610276Ssam 5710276Ssam %token 5810276Ssam A B C E F I 5910276Ssam L N P R S T 6010276Ssam 6110276Ssam SP CRLF COMMA STRING NUMBER 6210276Ssam 6310276Ssam USER PASS ACCT REIN QUIT PORT 6410276Ssam PASV TYPE STRU MODE RETR STOR 6510276Ssam APPE MLFL MAIL MSND MSOM MSAM 6610276Ssam MRSQ MRCP ALLO REST RNFR RNTO 6710276Ssam ABOR DELE CWD LIST NLST SITE 6810276Ssam STAT HELP NOOP XMKD XRMD XPWD 69*26045Sminshall XCUP STOU 7010276Ssam 7110276Ssam LEXERR 7210276Ssam 7310276Ssam %start cmd_list 7410276Ssam 7510276Ssam %% 7610276Ssam 7710276Ssam cmd_list: /* empty */ 7810276Ssam | cmd_list cmd 7910276Ssam ; 8010276Ssam 8110276Ssam cmd: USER SP username CRLF 8210276Ssam = { 8310276Ssam extern struct passwd *getpwnam(); 8410276Ssam 85*26045Sminshall logged_in = 0; 8610324Ssam if (strcmp($3, "ftp") == 0 || 8710324Ssam strcmp($3, "anonymous") == 0) { 8810324Ssam if ((pw = getpwnam("ftp")) != NULL) { 8910324Ssam guest = 1; 9010324Ssam reply(331, 9110276Ssam "Guest login ok, send ident as password."); 9210324Ssam } 93*26045Sminshall else { 94*26045Sminshall reply(530, "User %s unknown.", $3); 95*26045Sminshall } 9610694Ssam } else if (checkuser($3)) { 9710276Ssam guest = 0; 9810276Ssam pw = getpwnam($3); 99*26045Sminshall if (pw == NULL) { 100*26045Sminshall reply(530, "User %s unknown.", $3); 101*26045Sminshall } 102*26045Sminshall else { 103*26045Sminshall reply(331, "Password required for %s.", $3); 104*26045Sminshall } 10510276Ssam } 10610276Ssam free($3); 10710276Ssam } 10810276Ssam | PASS SP password CRLF 10910276Ssam = { 11010276Ssam pass($3); 11110276Ssam free($3); 11210276Ssam } 11310276Ssam | PORT SP host_port CRLF 11410276Ssam = { 11510320Ssam usedefault = 0; 116*26045Sminshall if (pdata > 0) { 117*26045Sminshall (void) close(pdata); 118*26045Sminshall } 119*26045Sminshall pdata = -1; 12010276Ssam ack($1); 12110276Ssam } 122*26045Sminshall | PASV CRLF 123*26045Sminshall = { 124*26045Sminshall passive(); 125*26045Sminshall } 12610276Ssam | TYPE SP type_code CRLF 12710276Ssam = { 12810276Ssam switch (cmd_type) { 12910276Ssam 13010276Ssam case TYPE_A: 13110276Ssam if (cmd_form == FORM_N) { 13210276Ssam reply(200, "Type set to A."); 13310276Ssam type = cmd_type; 13410276Ssam form = cmd_form; 13510276Ssam } else 13610276Ssam reply(504, "Form must be N."); 13710276Ssam break; 13810276Ssam 13910276Ssam case TYPE_E: 14010276Ssam reply(504, "Type E not implemented."); 14110276Ssam break; 14210276Ssam 14310276Ssam case TYPE_I: 14410276Ssam reply(200, "Type set to I."); 14510276Ssam type = cmd_type; 14610276Ssam break; 14710276Ssam 14810276Ssam case TYPE_L: 14910276Ssam if (cmd_bytesz == 8) { 15010276Ssam reply(200, 15110276Ssam "Type set to L (byte size 8)."); 15210276Ssam type = cmd_type; 15310276Ssam } else 15410276Ssam reply(504, "Byte size must be 8."); 15510276Ssam } 15610276Ssam } 15710276Ssam | STRU SP struct_code CRLF 15810276Ssam = { 15910276Ssam switch ($3) { 16010276Ssam 16110276Ssam case STRU_F: 16210276Ssam reply(200, "STRU F ok."); 16310276Ssam break; 16410276Ssam 16510276Ssam default: 16610276Ssam reply(502, "Unimplemented STRU type."); 16710276Ssam } 16810276Ssam } 16910276Ssam | MODE SP mode_code CRLF 17010276Ssam = { 17110276Ssam switch ($3) { 17210276Ssam 17310276Ssam case MODE_S: 17410276Ssam reply(200, "MODE S ok."); 17510276Ssam break; 17610276Ssam 17710276Ssam default: 17810276Ssam reply(502, "Unimplemented MODE type."); 17910276Ssam } 18010276Ssam } 18110276Ssam | ALLO SP NUMBER CRLF 18210276Ssam = { 18310276Ssam ack($1); 18410276Ssam } 18510276Ssam | RETR check_login SP pathname CRLF 18610276Ssam = { 18710302Ssam if ($2 && $4 != NULL) 18810276Ssam retrieve(0, $4); 18910302Ssam if ($4 != NULL) 19010302Ssam free($4); 19110276Ssam } 19210276Ssam | STOR check_login SP pathname CRLF 19310276Ssam = { 19410302Ssam if ($2 && $4 != NULL) 19510276Ssam store($4, "w"); 19610302Ssam if ($4 != NULL) 19710302Ssam free($4); 19810276Ssam } 19910276Ssam | APPE check_login SP pathname CRLF 20010276Ssam = { 20110302Ssam if ($2 && $4 != NULL) 20210276Ssam store($4, "a"); 20310302Ssam if ($4 != NULL) 20410302Ssam free($4); 20510276Ssam } 20610276Ssam | NLST check_login CRLF 20710276Ssam = { 20810276Ssam if ($2) 20911217Ssam retrieve("/bin/ls", ""); 21010276Ssam } 21110276Ssam | NLST check_login SP pathname CRLF 21210276Ssam = { 21310302Ssam if ($2 && $4 != NULL) 21411217Ssam retrieve("/bin/ls %s", $4); 21510302Ssam if ($4 != NULL) 21610302Ssam free($4); 21710276Ssam } 21810276Ssam | LIST check_login CRLF 21910276Ssam = { 22010276Ssam if ($2) 22110318Ssam retrieve("/bin/ls -lg", ""); 22210276Ssam } 22310276Ssam | LIST check_login SP pathname CRLF 22410276Ssam = { 22510302Ssam if ($2 && $4 != NULL) 22610318Ssam retrieve("/bin/ls -lg %s", $4); 22710302Ssam if ($4 != NULL) 22810302Ssam free($4); 22910276Ssam } 23010276Ssam | DELE check_login SP pathname CRLF 23110276Ssam = { 23210302Ssam if ($2 && $4 != NULL) 23310276Ssam delete($4); 23410302Ssam if ($4 != NULL) 23510302Ssam free($4); 23610276Ssam } 237*26045Sminshall | ABOR CRLF 238*26045Sminshall = { 239*26045Sminshall ack($1); 240*26045Sminshall } 24110276Ssam | CWD check_login CRLF 24210276Ssam = { 24310276Ssam if ($2) 24410276Ssam cwd(pw->pw_dir); 24510276Ssam } 24610276Ssam | CWD check_login SP pathname CRLF 24710276Ssam = { 24810302Ssam if ($2 && $4 != NULL) 24910276Ssam cwd($4); 25010302Ssam if ($4 != NULL) 25110302Ssam free($4); 25210276Ssam } 25310276Ssam | rename_cmd 25410276Ssam | HELP CRLF 25510276Ssam = { 25610276Ssam help(0); 25710276Ssam } 25810276Ssam | HELP SP STRING CRLF 25910276Ssam = { 26010276Ssam help($3); 26110276Ssam } 26210276Ssam | NOOP CRLF 26310276Ssam = { 26410276Ssam ack($1); 26510276Ssam } 26610276Ssam | XMKD check_login SP pathname CRLF 26710276Ssam = { 26810302Ssam if ($2 && $4 != NULL) 26910302Ssam makedir($4); 27010302Ssam if ($4 != NULL) 27110302Ssam free($4); 27210276Ssam } 27310276Ssam | XRMD check_login SP pathname CRLF 27410276Ssam = { 27510302Ssam if ($2 && $4 != NULL) 27610302Ssam removedir($4); 27710302Ssam if ($4 != NULL) 27810302Ssam free($4); 27910276Ssam } 28010276Ssam | XPWD check_login CRLF 28110276Ssam = { 28210276Ssam if ($2) 28310302Ssam pwd(); 28410276Ssam } 28510276Ssam | XCUP check_login CRLF 28610276Ssam = { 28710276Ssam if ($2) 28810276Ssam cwd(".."); 28910276Ssam } 290*26045Sminshall | STOU check_login SP pathname CRLF 291*26045Sminshall = { 292*26045Sminshall if ($2 && $4 != NULL) { 293*26045Sminshall unique++; 294*26045Sminshall store($4, "w"); 295*26045Sminshall unique = 0; 296*26045Sminshall } 297*26045Sminshall if ($4 != NULL) 298*26045Sminshall free($4); 299*26045Sminshall } 30010276Ssam | QUIT CRLF 30110276Ssam = { 30210276Ssam reply(221, "Goodbye."); 30313246Ssam dologout(0); 30410276Ssam } 30510276Ssam | error CRLF 30610276Ssam = { 30710276Ssam yyerrok; 30810276Ssam } 30910276Ssam ; 31010276Ssam 31110276Ssam username: STRING 31210276Ssam ; 31310276Ssam 31410276Ssam password: STRING 31510276Ssam ; 31610276Ssam 31710276Ssam byte_size: NUMBER 31810276Ssam ; 31910276Ssam 32010276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 32110276Ssam NUMBER COMMA NUMBER 32210276Ssam = { 32310276Ssam register char *a, *p; 32410276Ssam 32510276Ssam a = (char *)&data_dest.sin_addr; 32610276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 32710276Ssam p = (char *)&data_dest.sin_port; 32810276Ssam p[0] = $9; p[1] = $11; 32910324Ssam data_dest.sin_family = AF_INET; 33010276Ssam } 33110276Ssam ; 33210276Ssam 33310276Ssam form_code: N 33410276Ssam = { 33510276Ssam $$ = FORM_N; 33610276Ssam } 33710276Ssam | T 33810276Ssam = { 33910276Ssam $$ = FORM_T; 34010276Ssam } 34110276Ssam | C 34210276Ssam = { 34310276Ssam $$ = FORM_C; 34410276Ssam } 34510276Ssam ; 34610276Ssam 34710276Ssam type_code: A 34810276Ssam = { 34910276Ssam cmd_type = TYPE_A; 35010276Ssam cmd_form = FORM_N; 35110276Ssam } 35210276Ssam | A SP form_code 35310276Ssam = { 35410276Ssam cmd_type = TYPE_A; 35510276Ssam cmd_form = $3; 35610276Ssam } 35710276Ssam | E 35810276Ssam = { 35910276Ssam cmd_type = TYPE_E; 36010276Ssam cmd_form = FORM_N; 36110276Ssam } 36210276Ssam | E SP form_code 36310276Ssam = { 36410276Ssam cmd_type = TYPE_E; 36510276Ssam cmd_form = $3; 36610276Ssam } 36710276Ssam | I 36810276Ssam = { 36910276Ssam cmd_type = TYPE_I; 37010276Ssam } 37110276Ssam | L 37210276Ssam = { 37310276Ssam cmd_type = TYPE_L; 37410276Ssam cmd_bytesz = 8; 37510276Ssam } 37610276Ssam | L SP byte_size 37710276Ssam = { 37810276Ssam cmd_type = TYPE_L; 37910276Ssam cmd_bytesz = $3; 38010276Ssam } 38110276Ssam /* this is for a bug in the BBN ftp */ 38210276Ssam | L byte_size 38310276Ssam = { 38410276Ssam cmd_type = TYPE_L; 38510276Ssam cmd_bytesz = $2; 38610276Ssam } 38710276Ssam ; 38810276Ssam 38910276Ssam struct_code: F 39010276Ssam = { 39110276Ssam $$ = STRU_F; 39210276Ssam } 39310276Ssam | R 39410276Ssam = { 39510276Ssam $$ = STRU_R; 39610276Ssam } 39710276Ssam | P 39810276Ssam = { 39910276Ssam $$ = STRU_P; 40010276Ssam } 40110276Ssam ; 40210276Ssam 40310276Ssam mode_code: S 40410276Ssam = { 40510276Ssam $$ = MODE_S; 40610276Ssam } 40710276Ssam | B 40810276Ssam = { 40910276Ssam $$ = MODE_B; 41010276Ssam } 41110276Ssam | C 41210276Ssam = { 41310276Ssam $$ = MODE_C; 41410276Ssam } 41510276Ssam ; 41610276Ssam 41710276Ssam pathname: pathstring 41810276Ssam = { 41910276Ssam if ($1 && strncmp($1, "~", 1) == 0) { 42010276Ssam $$ = (int)*glob($1); 42110302Ssam if (globerr != NULL) { 42210276Ssam reply(550, globerr); 42310302Ssam $$ = NULL; 42410302Ssam } 42510276Ssam free($1); 42610276Ssam } else 42710276Ssam $$ = $1; 42810276Ssam } 42910276Ssam ; 43010276Ssam 43110276Ssam pathstring: STRING 43210276Ssam ; 43310276Ssam 43410276Ssam rename_cmd: rename_from rename_to 43510276Ssam = { 43610276Ssam if ($1 && $2) 43710276Ssam renamecmd($1, $2); 43810276Ssam else 43910276Ssam reply(503, "Bad sequence of commands."); 44010276Ssam if ($1) 44110276Ssam free($1); 44210276Ssam if ($2) 44310276Ssam free($2); 44410276Ssam } 44510276Ssam ; 44610276Ssam 44710276Ssam rename_from: RNFR check_login SP pathname CRLF 44810276Ssam = { 44910276Ssam char *from = 0, *renamefrom(); 45010276Ssam 45110302Ssam if ($2 && $4) 45210276Ssam from = renamefrom($4); 45310302Ssam if (from == 0 && $4) 45410276Ssam free($4); 45510276Ssam $$ = (int)from; 45610276Ssam } 45710276Ssam ; 45810276Ssam 45910276Ssam rename_to: RNTO SP pathname CRLF 46010276Ssam = { 46110276Ssam $$ = $3; 46210276Ssam } 46310276Ssam ; 46410276Ssam 46510276Ssam check_login: /* empty */ 46610276Ssam = { 46710276Ssam if (logged_in) 46810276Ssam $$ = 1; 46910276Ssam else { 47010276Ssam reply(530, "Please login with USER and PASS."); 47110276Ssam $$ = 0; 47210276Ssam } 47310276Ssam } 47410276Ssam ; 47510276Ssam 47610276Ssam %% 47710276Ssam 47810276Ssam extern jmp_buf errcatch; 47910276Ssam 48010276Ssam #define CMD 0 /* beginning of command */ 48110276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 48210276Ssam #define STR1 2 /* expect SP followed by STRING */ 48310276Ssam #define STR2 3 /* expect STRING */ 48410276Ssam #define OSTR 4 /* optional STRING */ 48510276Ssam 48610276Ssam struct tab { 48710276Ssam char *name; 48810276Ssam short token; 48910276Ssam short state; 49010276Ssam short implemented; /* 1 if command is implemented */ 49110276Ssam char *help; 49210276Ssam }; 49310276Ssam 49410276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 49510276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 49610276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 49710276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 49810276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 49910276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 50010276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 501*26045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 50210276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 50310276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 50410276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 50510276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 50610276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 50710276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 50810276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 50910276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 51010276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 51110276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 51210276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 51310276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 51410276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 51510276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 51610276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 51710276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 51810276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 519*26045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 52010276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 52110276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 52210276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 52310276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 52410276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 52510276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 52610276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 52710276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 52810276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 529*26045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 53010276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 531*26045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 53210276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 533*26045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 53410276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 535*26045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 53610276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 537*26045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 53810276Ssam { NULL, 0, 0, 0, 0 } 53910276Ssam }; 54010276Ssam 54110276Ssam struct tab * 54210276Ssam lookup(cmd) 54310276Ssam char *cmd; 54410276Ssam { 54510276Ssam register struct tab *p; 54610276Ssam 54710276Ssam for (p = cmdtab; p->name != NULL; p++) 54810276Ssam if (strcmp(cmd, p->name) == 0) 54910276Ssam return (p); 55010276Ssam return (0); 55110276Ssam } 55210276Ssam 55313033Ssam #include <arpa/telnet.h> 55410276Ssam 55510276Ssam /* 55610276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 55710276Ssam */ 55810276Ssam char * 55910276Ssam getline(s, n, iop) 56010276Ssam char *s; 56110276Ssam register FILE *iop; 56210276Ssam { 56310276Ssam register c; 564*26045Sminshall register char *cs, ch; 56510276Ssam 56610276Ssam cs = s; 567*26045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 568*26045Sminshall *cs++ = tmpline[c]; 569*26045Sminshall if (tmpline[c] == '\n') { 570*26045Sminshall *cs++ = '\0'; 571*26045Sminshall if (debug) { 572*26045Sminshall fprintf(stderr, "FTPD: command: %s", s); 573*26045Sminshall } 574*26045Sminshall tmpline[0] = '\0'; 575*26045Sminshall return(s); 576*26045Sminshall } 577*26045Sminshall if (c == 0) { 578*26045Sminshall tmpline[0] = '\0'; 579*26045Sminshall } 580*26045Sminshall } 581*26045Sminshall while (--n > 0 && read(fileno(iop),&ch,1) >= 0) { 582*26045Sminshall c = 0377 & ch; 58310276Ssam while (c == IAC) { 584*26045Sminshall read(fileno(iop),&ch,1); /* skip command */ 585*26045Sminshall read(fileno(iop),&ch,1); /* try next char */ 586*26045Sminshall c = 0377 & ch; 58710276Ssam } 58810276Ssam *cs++ = c; 58910276Ssam if (c=='\n') 59010276Ssam break; 59110276Ssam } 59210276Ssam if (c < 0 && cs == s) 59318303Sralph return (NULL); 59410276Ssam *cs++ = '\0'; 59511652Ssam if (debug) { 59611652Ssam fprintf(stderr, "FTPD: command: %s", s); 59711652Ssam if (c != '\n') 59811652Ssam putc('\n', stderr); 59911652Ssam fflush(stderr); 60011652Ssam } 60110276Ssam return (s); 60210276Ssam } 60310276Ssam 60411652Ssam static int 60511652Ssam toolong() 60611652Ssam { 60711652Ssam long now; 60811652Ssam extern char *ctime(); 60911652Ssam 61011652Ssam reply(421, 61111652Ssam "Timeout (%d seconds): closing control connection.", timeout); 61211652Ssam time(&now); 61311652Ssam if (logging) { 61411652Ssam fprintf(stderr, 61511652Ssam "FTPD: User %s timed out after %d seconds at %s", 61611652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 61711652Ssam fflush(stderr); 61811652Ssam } 61913246Ssam dologout(1); 62011652Ssam } 62111652Ssam 62210276Ssam yylex() 62310276Ssam { 62410276Ssam static int cpos, state; 62510276Ssam register char *cp; 62610276Ssam register struct tab *p; 62710276Ssam int n; 62810276Ssam char c; 62910276Ssam 63010276Ssam for (;;) { 63110276Ssam switch (state) { 63210276Ssam 63310276Ssam case CMD: 63411652Ssam signal(SIGALRM, toolong); 63511652Ssam alarm(timeout); 63610276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 63710276Ssam reply(221, "You could at least say goodbye."); 63813246Ssam dologout(0); 63910276Ssam } 64011652Ssam alarm(0); 64110276Ssam if (index(cbuf, '\r')) { 64210276Ssam cp = index(cbuf, '\r'); 64310276Ssam cp[0] = '\n'; cp[1] = 0; 64410276Ssam } 64510276Ssam if (index(cbuf, ' ')) 64610276Ssam cpos = index(cbuf, ' ') - cbuf; 64710276Ssam else 648*26045Sminshall cpos = index(cbuf, '\n') - cbuf; 649*26045Sminshall if (cpos == 0) { 65010276Ssam cpos = 4; 651*26045Sminshall } 65210276Ssam c = cbuf[cpos]; 65310276Ssam cbuf[cpos] = '\0'; 65410276Ssam upper(cbuf); 65510276Ssam p = lookup(cbuf); 65610276Ssam cbuf[cpos] = c; 65710276Ssam if (p != 0) { 65810276Ssam if (p->implemented == 0) { 65910276Ssam nack(p->name); 66010276Ssam longjmp(errcatch); 66110276Ssam /* NOTREACHED */ 66210276Ssam } 66310276Ssam state = p->state; 66410276Ssam yylval = (int) p->name; 66510276Ssam return (p->token); 66610276Ssam } 66710276Ssam break; 66810276Ssam 66910276Ssam case OSTR: 67010276Ssam if (cbuf[cpos] == '\n') { 67110276Ssam state = CMD; 67210276Ssam return (CRLF); 67310276Ssam } 67410276Ssam /* FALL THRU */ 67510276Ssam 67610276Ssam case STR1: 67710276Ssam if (cbuf[cpos] == ' ') { 67810276Ssam cpos++; 67910276Ssam state = STR2; 68010276Ssam return (SP); 68110276Ssam } 68210276Ssam break; 68310276Ssam 68410276Ssam case STR2: 68510276Ssam cp = &cbuf[cpos]; 68610276Ssam n = strlen(cp); 68710276Ssam cpos += n - 1; 68810276Ssam /* 68910276Ssam * Make sure the string is nonempty and \n terminated. 69010276Ssam */ 69110276Ssam if (n > 1 && cbuf[cpos] == '\n') { 69210276Ssam cbuf[cpos] = '\0'; 69310276Ssam yylval = copy(cp); 69410276Ssam cbuf[cpos] = '\n'; 69510276Ssam state = ARGS; 69610276Ssam return (STRING); 69710276Ssam } 69810276Ssam break; 69910276Ssam 70010276Ssam case ARGS: 70110276Ssam if (isdigit(cbuf[cpos])) { 70210276Ssam cp = &cbuf[cpos]; 70310276Ssam while (isdigit(cbuf[++cpos])) 70410276Ssam ; 70510276Ssam c = cbuf[cpos]; 70610276Ssam cbuf[cpos] = '\0'; 70710276Ssam yylval = atoi(cp); 70810276Ssam cbuf[cpos] = c; 70910276Ssam return (NUMBER); 71010276Ssam } 71110276Ssam switch (cbuf[cpos++]) { 71210276Ssam 71310276Ssam case '\n': 71410276Ssam state = CMD; 71510276Ssam return (CRLF); 71610276Ssam 71710276Ssam case ' ': 71810276Ssam return (SP); 71910276Ssam 72010276Ssam case ',': 72110276Ssam return (COMMA); 72210276Ssam 72310276Ssam case 'A': 72410276Ssam case 'a': 72510276Ssam return (A); 72610276Ssam 72710276Ssam case 'B': 72810276Ssam case 'b': 72910276Ssam return (B); 73010276Ssam 73110276Ssam case 'C': 73210276Ssam case 'c': 73310276Ssam return (C); 73410276Ssam 73510276Ssam case 'E': 73610276Ssam case 'e': 73710276Ssam return (E); 73810276Ssam 73910276Ssam case 'F': 74010276Ssam case 'f': 74110276Ssam return (F); 74210276Ssam 74310276Ssam case 'I': 74410276Ssam case 'i': 74510276Ssam return (I); 74610276Ssam 74710276Ssam case 'L': 74810276Ssam case 'l': 74910276Ssam return (L); 75010276Ssam 75110276Ssam case 'N': 75210276Ssam case 'n': 75310276Ssam return (N); 75410276Ssam 75510276Ssam case 'P': 75610276Ssam case 'p': 75710276Ssam return (P); 75810276Ssam 75910276Ssam case 'R': 76010276Ssam case 'r': 76110276Ssam return (R); 76210276Ssam 76310276Ssam case 'S': 76410276Ssam case 's': 76510276Ssam return (S); 76610276Ssam 76710276Ssam case 'T': 76810276Ssam case 't': 76910276Ssam return (T); 77010276Ssam 77110276Ssam } 77210276Ssam break; 77310276Ssam 77410276Ssam default: 77510276Ssam fatal("Unknown state in scanner."); 77610276Ssam } 77710276Ssam yyerror(); 77810276Ssam state = CMD; 77910276Ssam longjmp(errcatch); 78010276Ssam } 78110276Ssam } 78210276Ssam 78310276Ssam upper(s) 78410276Ssam char *s; 78510276Ssam { 78610276Ssam while (*s != '\0') { 78710276Ssam if (islower(*s)) 78810276Ssam *s = toupper(*s); 78910276Ssam s++; 79010276Ssam } 79110276Ssam } 79210276Ssam 79310276Ssam copy(s) 79410276Ssam char *s; 79510276Ssam { 79610276Ssam char *p; 79710276Ssam extern char *malloc(); 79810276Ssam 79910276Ssam p = malloc(strlen(s) + 1); 80010276Ssam if (p == NULL) 80110276Ssam fatal("Ran out of memory."); 80210276Ssam strcpy(p, s); 80310276Ssam return ((int)p); 80410276Ssam } 80510276Ssam 80610276Ssam help(s) 80710276Ssam char *s; 80810276Ssam { 80910276Ssam register struct tab *c; 81010276Ssam register int width, NCMDS; 81110276Ssam 81210276Ssam width = 0, NCMDS = 0; 81310276Ssam for (c = cmdtab; c->name != NULL; c++) { 81410276Ssam int len = strlen(c->name); 81510276Ssam 81610276Ssam if (c->implemented == 0) 81710276Ssam len++; 81810276Ssam if (len > width) 81910276Ssam width = len; 82010276Ssam NCMDS++; 82110276Ssam } 82210276Ssam width = (width + 8) &~ 7; 82310276Ssam if (s == 0) { 82410276Ssam register int i, j, w; 82510276Ssam int columns, lines; 82610276Ssam 82710276Ssam lreply(214, 82810276Ssam "The following commands are recognized (* =>'s unimplemented)."); 82910276Ssam columns = 76 / width; 83010276Ssam if (columns == 0) 83110276Ssam columns = 1; 83210276Ssam lines = (NCMDS + columns - 1) / columns; 83310276Ssam for (i = 0; i < lines; i++) { 83410276Ssam printf(" "); 83510276Ssam for (j = 0; j < columns; j++) { 83610276Ssam c = cmdtab + j * lines + i; 83710276Ssam printf("%s%c", c->name, 83810276Ssam c->implemented ? ' ' : '*'); 83910302Ssam if (c + lines >= &cmdtab[NCMDS]) 84010276Ssam break; 84110276Ssam w = strlen(c->name); 84210276Ssam while (w < width) { 84310276Ssam putchar(' '); 84410276Ssam w++; 84510276Ssam } 84610276Ssam } 84710276Ssam printf("\r\n"); 84810276Ssam } 84910276Ssam fflush(stdout); 85010276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 85110276Ssam return; 85210276Ssam } 85310276Ssam upper(s); 85410276Ssam c = lookup(s); 85510276Ssam if (c == (struct tab *)0) { 85610276Ssam reply(504, "Unknown command %s.", s); 85710276Ssam return; 85810276Ssam } 85910276Ssam if (c->implemented) 86010276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 86110276Ssam else 86210276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 86310276Ssam } 864