110276Ssam /* 226045Sminshall * 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*27107Smckusick static char sccsid[] = "@(#)ftpcmd.y 5.5 (Berkeley) 04/16/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> 3026494Sminshall #include <syslog.h> 3110276Ssam 3210276Ssam extern struct sockaddr_in data_dest; 3310276Ssam extern int logged_in; 3410276Ssam extern struct passwd *pw; 3510276Ssam extern int guest; 3610276Ssam extern int logging; 3710276Ssam extern int type; 3810276Ssam extern int form; 3910276Ssam extern int debug; 4011652Ssam extern int timeout; 4126045Sminshall extern int pdata; 4210276Ssam extern char hostname[]; 4310276Ssam extern char *globerr; 4410320Ssam extern int usedefault; 4526045Sminshall extern int unique; 4626045Sminshall extern int transflag; 4726045Sminshall extern char tmpline[]; 4810276Ssam char **glob(); 4910276Ssam 5010276Ssam static int cmd_type; 5110276Ssam static int cmd_form; 5210276Ssam static int cmd_bytesz; 5326045Sminshall char cbuf[512]; 5410276Ssam 5510276Ssam char *index(); 5610276Ssam %} 5710276Ssam 5810276Ssam %token 5910276Ssam A B C E F I 6010276Ssam L N P R S T 6110276Ssam 6210276Ssam SP CRLF COMMA STRING NUMBER 6310276Ssam 6410276Ssam USER PASS ACCT REIN QUIT PORT 6510276Ssam PASV TYPE STRU MODE RETR STOR 6610276Ssam APPE MLFL MAIL MSND MSOM MSAM 6710276Ssam MRSQ MRCP ALLO REST RNFR RNTO 6810276Ssam ABOR DELE CWD LIST NLST SITE 6910276Ssam STAT HELP NOOP XMKD XRMD XPWD 7026045Sminshall XCUP STOU 7110276Ssam 7210276Ssam LEXERR 7310276Ssam 7410276Ssam %start cmd_list 7510276Ssam 7610276Ssam %% 7710276Ssam 7810276Ssam cmd_list: /* empty */ 7910276Ssam | cmd_list cmd 8010276Ssam ; 8110276Ssam 8210276Ssam cmd: USER SP username CRLF 8310276Ssam = { 8410276Ssam extern struct passwd *getpwnam(); 8510276Ssam 8626045Sminshall logged_in = 0; 8726494Sminshall if (strcmp((char *) $3, "ftp") == 0 || 8826494Sminshall strcmp((char *) $3, "anonymous") == 0) { 8910324Ssam if ((pw = getpwnam("ftp")) != NULL) { 9010324Ssam guest = 1; 9110324Ssam reply(331, 9210276Ssam "Guest login ok, send ident as password."); 9310324Ssam } 9426045Sminshall else { 9526045Sminshall reply(530, "User %s unknown.", $3); 9626045Sminshall } 9726494Sminshall } else if (checkuser((char *) $3)) { 9810276Ssam guest = 0; 9926494Sminshall pw = getpwnam((char *) $3); 10026045Sminshall if (pw == NULL) { 10126045Sminshall reply(530, "User %s unknown.", $3); 10226045Sminshall } 10326045Sminshall else { 10426045Sminshall reply(331, "Password required for %s.", $3); 10526045Sminshall } 10610276Ssam } 10726494Sminshall free((char *) $3); 10810276Ssam } 10910276Ssam | PASS SP password CRLF 11010276Ssam = { 11126494Sminshall pass((char *) $3); 11226494Sminshall free((char *) $3); 11310276Ssam } 11410276Ssam | PORT SP host_port CRLF 11510276Ssam = { 11610320Ssam usedefault = 0; 11726045Sminshall if (pdata > 0) { 11826045Sminshall (void) close(pdata); 11926045Sminshall } 12026045Sminshall pdata = -1; 121*27107Smckusick reply(200, "PORT command successful."); 12210276Ssam } 12326045Sminshall | PASV CRLF 12426045Sminshall = { 12526045Sminshall passive(); 12626045Sminshall } 12710276Ssam | TYPE SP type_code CRLF 12810276Ssam = { 12910276Ssam switch (cmd_type) { 13010276Ssam 13110276Ssam case TYPE_A: 13210276Ssam if (cmd_form == FORM_N) { 13310276Ssam reply(200, "Type set to A."); 13410276Ssam type = cmd_type; 13510276Ssam form = cmd_form; 13610276Ssam } else 13710276Ssam reply(504, "Form must be N."); 13810276Ssam break; 13910276Ssam 14010276Ssam case TYPE_E: 14110276Ssam reply(504, "Type E not implemented."); 14210276Ssam break; 14310276Ssam 14410276Ssam case TYPE_I: 14510276Ssam reply(200, "Type set to I."); 14610276Ssam type = cmd_type; 14710276Ssam break; 14810276Ssam 14910276Ssam case TYPE_L: 15010276Ssam if (cmd_bytesz == 8) { 15110276Ssam reply(200, 15210276Ssam "Type set to L (byte size 8)."); 15310276Ssam type = cmd_type; 15410276Ssam } else 15510276Ssam reply(504, "Byte size must be 8."); 15610276Ssam } 15710276Ssam } 15810276Ssam | STRU SP struct_code CRLF 15910276Ssam = { 16010276Ssam switch ($3) { 16110276Ssam 16210276Ssam case STRU_F: 16310276Ssam reply(200, "STRU F ok."); 16410276Ssam break; 16510276Ssam 16610276Ssam default: 167*27107Smckusick reply(504, "Unimplemented STRU type."); 16810276Ssam } 16910276Ssam } 17010276Ssam | MODE SP mode_code CRLF 17110276Ssam = { 17210276Ssam switch ($3) { 17310276Ssam 17410276Ssam case MODE_S: 17510276Ssam reply(200, "MODE S ok."); 17610276Ssam break; 17710276Ssam 17810276Ssam default: 17910276Ssam reply(502, "Unimplemented MODE type."); 18010276Ssam } 18110276Ssam } 18210276Ssam | ALLO SP NUMBER CRLF 18310276Ssam = { 184*27107Smckusick reply(202, "ALLO command ignored."); 18510276Ssam } 18610276Ssam | RETR check_login SP pathname CRLF 18710276Ssam = { 18810302Ssam if ($2 && $4 != NULL) 18926494Sminshall retrieve((char *) 0, (char *) $4); 19010302Ssam if ($4 != NULL) 19126494Sminshall free((char *) $4); 19210276Ssam } 19310276Ssam | STOR check_login SP pathname CRLF 19410276Ssam = { 19510302Ssam if ($2 && $4 != NULL) 19626494Sminshall store((char *) $4, "w"); 19710302Ssam if ($4 != NULL) 19826494Sminshall free((char *) $4); 19910276Ssam } 20010276Ssam | APPE check_login SP pathname CRLF 20110276Ssam = { 20210302Ssam if ($2 && $4 != NULL) 20326494Sminshall store((char *) $4, "a"); 20410302Ssam if ($4 != NULL) 20526494Sminshall free((char *) $4); 20610276Ssam } 20710276Ssam | NLST check_login CRLF 20810276Ssam = { 20910276Ssam if ($2) 21011217Ssam retrieve("/bin/ls", ""); 21110276Ssam } 21210276Ssam | NLST check_login SP pathname CRLF 21310276Ssam = { 21410302Ssam if ($2 && $4 != NULL) 21526494Sminshall retrieve("/bin/ls %s", (char *) $4); 21610302Ssam if ($4 != NULL) 21726494Sminshall free((char *) $4); 21810276Ssam } 21910276Ssam | LIST check_login CRLF 22010276Ssam = { 22110276Ssam if ($2) 22210318Ssam retrieve("/bin/ls -lg", ""); 22310276Ssam } 22410276Ssam | LIST check_login SP pathname CRLF 22510276Ssam = { 22610302Ssam if ($2 && $4 != NULL) 22726494Sminshall retrieve("/bin/ls -lg %s", (char *) $4); 22810302Ssam if ($4 != NULL) 22926494Sminshall free((char *) $4); 23010276Ssam } 23110276Ssam | DELE check_login SP pathname CRLF 23210276Ssam = { 23310302Ssam if ($2 && $4 != NULL) 23426494Sminshall delete((char *) $4); 23510302Ssam if ($4 != NULL) 23626494Sminshall free((char *) $4); 23710276Ssam } 23826045Sminshall | ABOR CRLF 23926045Sminshall = { 240*27107Smckusick reply(225, "ABOR command successful."); 24126045Sminshall } 24210276Ssam | CWD check_login CRLF 24310276Ssam = { 24410276Ssam if ($2) 24510276Ssam cwd(pw->pw_dir); 24610276Ssam } 24710276Ssam | CWD check_login SP pathname CRLF 24810276Ssam = { 24910302Ssam if ($2 && $4 != NULL) 25026494Sminshall cwd((char *) $4); 25110302Ssam if ($4 != NULL) 25226494Sminshall free((char *) $4); 25310276Ssam } 25410276Ssam | rename_cmd 25510276Ssam | HELP CRLF 25610276Ssam = { 25726494Sminshall help((char *) 0); 25810276Ssam } 25910276Ssam | HELP SP STRING CRLF 26010276Ssam = { 26126494Sminshall help((char *) $3); 26210276Ssam } 26310276Ssam | NOOP CRLF 26410276Ssam = { 265*27107Smckusick reply(200, "NOOP command successful."); 26610276Ssam } 26710276Ssam | XMKD check_login SP pathname CRLF 26810276Ssam = { 26910302Ssam if ($2 && $4 != NULL) 27026494Sminshall makedir((char *) $4); 27110302Ssam if ($4 != NULL) 27226494Sminshall free((char *) $4); 27310276Ssam } 27410276Ssam | XRMD check_login SP pathname CRLF 27510276Ssam = { 27610302Ssam if ($2 && $4 != NULL) 27726494Sminshall removedir((char *) $4); 27810302Ssam if ($4 != NULL) 27926494Sminshall free((char *) $4); 28010276Ssam } 28110276Ssam | XPWD check_login CRLF 28210276Ssam = { 28310276Ssam if ($2) 28410302Ssam pwd(); 28510276Ssam } 28610276Ssam | XCUP check_login CRLF 28710276Ssam = { 28810276Ssam if ($2) 28910276Ssam cwd(".."); 29010276Ssam } 29126045Sminshall | STOU check_login SP pathname CRLF 29226045Sminshall = { 29326045Sminshall if ($2 && $4 != NULL) { 29426045Sminshall unique++; 29526494Sminshall store((char *) $4, "w"); 29626045Sminshall unique = 0; 29726045Sminshall } 29826045Sminshall if ($4 != NULL) 29926494Sminshall free((char *) $4); 30026045Sminshall } 30110276Ssam | QUIT CRLF 30210276Ssam = { 30310276Ssam reply(221, "Goodbye."); 30413246Ssam dologout(0); 30510276Ssam } 30610276Ssam | error CRLF 30710276Ssam = { 30810276Ssam yyerrok; 30910276Ssam } 31010276Ssam ; 31110276Ssam 31210276Ssam username: STRING 31310276Ssam ; 31410276Ssam 31510276Ssam password: STRING 31610276Ssam ; 31710276Ssam 31810276Ssam byte_size: NUMBER 31910276Ssam ; 32010276Ssam 32110276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 32210276Ssam NUMBER COMMA NUMBER 32310276Ssam = { 32410276Ssam register char *a, *p; 32510276Ssam 32610276Ssam a = (char *)&data_dest.sin_addr; 32710276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 32810276Ssam p = (char *)&data_dest.sin_port; 32910276Ssam p[0] = $9; p[1] = $11; 33010324Ssam data_dest.sin_family = AF_INET; 33110276Ssam } 33210276Ssam ; 33310276Ssam 33410276Ssam form_code: N 33510276Ssam = { 33610276Ssam $$ = FORM_N; 33710276Ssam } 33810276Ssam | T 33910276Ssam = { 34010276Ssam $$ = FORM_T; 34110276Ssam } 34210276Ssam | C 34310276Ssam = { 34410276Ssam $$ = FORM_C; 34510276Ssam } 34610276Ssam ; 34710276Ssam 34810276Ssam type_code: A 34910276Ssam = { 35010276Ssam cmd_type = TYPE_A; 35110276Ssam cmd_form = FORM_N; 35210276Ssam } 35310276Ssam | A SP form_code 35410276Ssam = { 35510276Ssam cmd_type = TYPE_A; 35610276Ssam cmd_form = $3; 35710276Ssam } 35810276Ssam | E 35910276Ssam = { 36010276Ssam cmd_type = TYPE_E; 36110276Ssam cmd_form = FORM_N; 36210276Ssam } 36310276Ssam | E SP form_code 36410276Ssam = { 36510276Ssam cmd_type = TYPE_E; 36610276Ssam cmd_form = $3; 36710276Ssam } 36810276Ssam | I 36910276Ssam = { 37010276Ssam cmd_type = TYPE_I; 37110276Ssam } 37210276Ssam | L 37310276Ssam = { 37410276Ssam cmd_type = TYPE_L; 37510276Ssam cmd_bytesz = 8; 37610276Ssam } 37710276Ssam | L SP byte_size 37810276Ssam = { 37910276Ssam cmd_type = TYPE_L; 38010276Ssam cmd_bytesz = $3; 38110276Ssam } 38210276Ssam /* this is for a bug in the BBN ftp */ 38310276Ssam | L byte_size 38410276Ssam = { 38510276Ssam cmd_type = TYPE_L; 38610276Ssam cmd_bytesz = $2; 38710276Ssam } 38810276Ssam ; 38910276Ssam 39010276Ssam struct_code: F 39110276Ssam = { 39210276Ssam $$ = STRU_F; 39310276Ssam } 39410276Ssam | R 39510276Ssam = { 39610276Ssam $$ = STRU_R; 39710276Ssam } 39810276Ssam | P 39910276Ssam = { 40010276Ssam $$ = STRU_P; 40110276Ssam } 40210276Ssam ; 40310276Ssam 40410276Ssam mode_code: S 40510276Ssam = { 40610276Ssam $$ = MODE_S; 40710276Ssam } 40810276Ssam | B 40910276Ssam = { 41010276Ssam $$ = MODE_B; 41110276Ssam } 41210276Ssam | C 41310276Ssam = { 41410276Ssam $$ = MODE_C; 41510276Ssam } 41610276Ssam ; 41710276Ssam 41810276Ssam pathname: pathstring 41910276Ssam = { 420*27107Smckusick /* 421*27107Smckusick * Problem: this production is used for all pathname 422*27107Smckusick * processing, but only gives a 550 error reply. 423*27107Smckusick * This is a valid reply in some cases but not in others. 424*27107Smckusick */ 42526494Sminshall if ($1 && strncmp((char *) $1, "~", 1) == 0) { 42626494Sminshall $$ = (int)*glob((char *) $1); 42710302Ssam if (globerr != NULL) { 42810276Ssam reply(550, globerr); 42910302Ssam $$ = NULL; 43010302Ssam } 43126494Sminshall free((char *) $1); 43210276Ssam } else 43310276Ssam $$ = $1; 43410276Ssam } 43510276Ssam ; 43610276Ssam 43710276Ssam pathstring: STRING 43810276Ssam ; 43910276Ssam 44010276Ssam rename_cmd: rename_from rename_to 44110276Ssam = { 44210276Ssam if ($1 && $2) 44326494Sminshall renamecmd((char *) $1, (char *) $2); 44410276Ssam else 44510276Ssam reply(503, "Bad sequence of commands."); 44610276Ssam if ($1) 44726494Sminshall free((char *) $1); 44810276Ssam if ($2) 44926494Sminshall free((char *) $2); 45010276Ssam } 45110276Ssam ; 45210276Ssam 45310276Ssam rename_from: RNFR check_login SP pathname CRLF 45410276Ssam = { 45510276Ssam char *from = 0, *renamefrom(); 45610276Ssam 45710302Ssam if ($2 && $4) 45826494Sminshall from = renamefrom((char *) $4); 45910302Ssam if (from == 0 && $4) 46026494Sminshall free((char *) $4); 46110276Ssam $$ = (int)from; 46210276Ssam } 46310276Ssam ; 46410276Ssam 46510276Ssam rename_to: RNTO SP pathname CRLF 46610276Ssam = { 46710276Ssam $$ = $3; 46810276Ssam } 46910276Ssam ; 47010276Ssam 47110276Ssam check_login: /* empty */ 47210276Ssam = { 47310276Ssam if (logged_in) 47410276Ssam $$ = 1; 47510276Ssam else { 47610276Ssam reply(530, "Please login with USER and PASS."); 47710276Ssam $$ = 0; 47810276Ssam } 47910276Ssam } 48010276Ssam ; 48110276Ssam 48210276Ssam %% 48310276Ssam 48410276Ssam extern jmp_buf errcatch; 48510276Ssam 48610276Ssam #define CMD 0 /* beginning of command */ 48710276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 48810276Ssam #define STR1 2 /* expect SP followed by STRING */ 48910276Ssam #define STR2 3 /* expect STRING */ 49010276Ssam #define OSTR 4 /* optional STRING */ 49110276Ssam 49210276Ssam struct tab { 49310276Ssam char *name; 49410276Ssam short token; 49510276Ssam short state; 49610276Ssam short implemented; /* 1 if command is implemented */ 49710276Ssam char *help; 49810276Ssam }; 49910276Ssam 50010276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 50110276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 50210276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 50310276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 50410276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 50510276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 50610276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 50726045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 50810276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 50910276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 51010276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 51110276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 51210276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 51310276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 51410276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 51510276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 51610276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 51710276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 51810276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 51910276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 52010276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 52110276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 52210276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 52310276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 52410276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 52526045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 52610276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 52710276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 52810276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 52910276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 53010276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 53110276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 53210276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 53310276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 53410276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 53526045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 53610276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 53726045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 53810276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 53926045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 54010276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 54126045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54210276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54326045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 54410276Ssam { NULL, 0, 0, 0, 0 } 54510276Ssam }; 54610276Ssam 54710276Ssam struct tab * 54810276Ssam lookup(cmd) 54910276Ssam char *cmd; 55010276Ssam { 55110276Ssam register struct tab *p; 55210276Ssam 55310276Ssam for (p = cmdtab; p->name != NULL; p++) 55410276Ssam if (strcmp(cmd, p->name) == 0) 55510276Ssam return (p); 55610276Ssam return (0); 55710276Ssam } 55810276Ssam 55913033Ssam #include <arpa/telnet.h> 56010276Ssam 56110276Ssam /* 56210276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 56310276Ssam */ 56410276Ssam char * 56510276Ssam getline(s, n, iop) 56610276Ssam char *s; 56710276Ssam register FILE *iop; 56810276Ssam { 56910276Ssam register c; 57026494Sminshall register char *cs; 57126494Sminshall char ch; 57210276Ssam 57310276Ssam cs = s; 57426045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 57526045Sminshall *cs++ = tmpline[c]; 57626045Sminshall if (tmpline[c] == '\n') { 57726045Sminshall *cs++ = '\0'; 57826045Sminshall if (debug) { 57926494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 58026045Sminshall } 58126045Sminshall tmpline[0] = '\0'; 58226045Sminshall return(s); 58326045Sminshall } 58426045Sminshall if (c == 0) { 58526045Sminshall tmpline[0] = '\0'; 58626045Sminshall } 58726045Sminshall } 58826045Sminshall while (--n > 0 && read(fileno(iop),&ch,1) >= 0) { 58926045Sminshall c = 0377 & ch; 59010276Ssam while (c == IAC) { 59126494Sminshall (void) read(fileno(iop),&ch,1); /* skip command */ 59226494Sminshall (void) read(fileno(iop),&ch,1); /* try next char */ 59326045Sminshall c = 0377 & ch; 59410276Ssam } 59510276Ssam *cs++ = c; 59610276Ssam if (c=='\n') 59710276Ssam break; 59810276Ssam } 59910276Ssam if (c < 0 && cs == s) 60018303Sralph return (NULL); 60110276Ssam *cs++ = '\0'; 60211652Ssam if (debug) { 60326494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 60411652Ssam } 60510276Ssam return (s); 60610276Ssam } 60710276Ssam 60811652Ssam static int 60911652Ssam toolong() 61011652Ssam { 61126494Sminshall time_t now; 61211652Ssam extern char *ctime(); 61326494Sminshall extern time_t time(); 61411652Ssam 61511652Ssam reply(421, 61611652Ssam "Timeout (%d seconds): closing control connection.", timeout); 61726494Sminshall (void) time(&now); 61811652Ssam if (logging) { 61926494Sminshall syslog(LOG_INFO, 62011652Ssam "FTPD: User %s timed out after %d seconds at %s", 62111652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 62211652Ssam } 62313246Ssam dologout(1); 62411652Ssam } 62511652Ssam 62610276Ssam yylex() 62710276Ssam { 62810276Ssam static int cpos, state; 62910276Ssam register char *cp; 63010276Ssam register struct tab *p; 63110276Ssam int n; 63210276Ssam char c; 63310276Ssam 63410276Ssam for (;;) { 63510276Ssam switch (state) { 63610276Ssam 63710276Ssam case CMD: 63826494Sminshall (void) signal(SIGALRM, toolong); 63926494Sminshall (void) alarm((unsigned) timeout); 64010276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 64110276Ssam reply(221, "You could at least say goodbye."); 64213246Ssam dologout(0); 64310276Ssam } 64426494Sminshall (void) alarm(0); 64510276Ssam if (index(cbuf, '\r')) { 64610276Ssam cp = index(cbuf, '\r'); 64710276Ssam cp[0] = '\n'; cp[1] = 0; 64810276Ssam } 64910276Ssam if (index(cbuf, ' ')) 65010276Ssam cpos = index(cbuf, ' ') - cbuf; 65110276Ssam else 65226045Sminshall cpos = index(cbuf, '\n') - cbuf; 65326045Sminshall if (cpos == 0) { 65410276Ssam cpos = 4; 65526045Sminshall } 65610276Ssam c = cbuf[cpos]; 65710276Ssam cbuf[cpos] = '\0'; 65810276Ssam upper(cbuf); 65910276Ssam p = lookup(cbuf); 66010276Ssam cbuf[cpos] = c; 66110276Ssam if (p != 0) { 66210276Ssam if (p->implemented == 0) { 66310276Ssam nack(p->name); 66426494Sminshall longjmp(errcatch,0); 66510276Ssam /* NOTREACHED */ 66610276Ssam } 66710276Ssam state = p->state; 66810276Ssam yylval = (int) p->name; 66910276Ssam return (p->token); 67010276Ssam } 67110276Ssam break; 67210276Ssam 67310276Ssam case OSTR: 67410276Ssam if (cbuf[cpos] == '\n') { 67510276Ssam state = CMD; 67610276Ssam return (CRLF); 67710276Ssam } 67810276Ssam /* FALL THRU */ 67910276Ssam 68010276Ssam case STR1: 68110276Ssam if (cbuf[cpos] == ' ') { 68210276Ssam cpos++; 68310276Ssam state = STR2; 68410276Ssam return (SP); 68510276Ssam } 68610276Ssam break; 68710276Ssam 68810276Ssam case STR2: 68910276Ssam cp = &cbuf[cpos]; 69010276Ssam n = strlen(cp); 69110276Ssam cpos += n - 1; 69210276Ssam /* 69310276Ssam * Make sure the string is nonempty and \n terminated. 69410276Ssam */ 69510276Ssam if (n > 1 && cbuf[cpos] == '\n') { 69610276Ssam cbuf[cpos] = '\0'; 69710276Ssam yylval = copy(cp); 69810276Ssam cbuf[cpos] = '\n'; 69910276Ssam state = ARGS; 70010276Ssam return (STRING); 70110276Ssam } 70210276Ssam break; 70310276Ssam 70410276Ssam case ARGS: 70510276Ssam if (isdigit(cbuf[cpos])) { 70610276Ssam cp = &cbuf[cpos]; 70710276Ssam while (isdigit(cbuf[++cpos])) 70810276Ssam ; 70910276Ssam c = cbuf[cpos]; 71010276Ssam cbuf[cpos] = '\0'; 71110276Ssam yylval = atoi(cp); 71210276Ssam cbuf[cpos] = c; 71310276Ssam return (NUMBER); 71410276Ssam } 71510276Ssam switch (cbuf[cpos++]) { 71610276Ssam 71710276Ssam case '\n': 71810276Ssam state = CMD; 71910276Ssam return (CRLF); 72010276Ssam 72110276Ssam case ' ': 72210276Ssam return (SP); 72310276Ssam 72410276Ssam case ',': 72510276Ssam return (COMMA); 72610276Ssam 72710276Ssam case 'A': 72810276Ssam case 'a': 72910276Ssam return (A); 73010276Ssam 73110276Ssam case 'B': 73210276Ssam case 'b': 73310276Ssam return (B); 73410276Ssam 73510276Ssam case 'C': 73610276Ssam case 'c': 73710276Ssam return (C); 73810276Ssam 73910276Ssam case 'E': 74010276Ssam case 'e': 74110276Ssam return (E); 74210276Ssam 74310276Ssam case 'F': 74410276Ssam case 'f': 74510276Ssam return (F); 74610276Ssam 74710276Ssam case 'I': 74810276Ssam case 'i': 74910276Ssam return (I); 75010276Ssam 75110276Ssam case 'L': 75210276Ssam case 'l': 75310276Ssam return (L); 75410276Ssam 75510276Ssam case 'N': 75610276Ssam case 'n': 75710276Ssam return (N); 75810276Ssam 75910276Ssam case 'P': 76010276Ssam case 'p': 76110276Ssam return (P); 76210276Ssam 76310276Ssam case 'R': 76410276Ssam case 'r': 76510276Ssam return (R); 76610276Ssam 76710276Ssam case 'S': 76810276Ssam case 's': 76910276Ssam return (S); 77010276Ssam 77110276Ssam case 'T': 77210276Ssam case 't': 77310276Ssam return (T); 77410276Ssam 77510276Ssam } 77610276Ssam break; 77710276Ssam 77810276Ssam default: 77910276Ssam fatal("Unknown state in scanner."); 78010276Ssam } 78126494Sminshall yyerror((char *) 0); 78210276Ssam state = CMD; 78326494Sminshall longjmp(errcatch,0); 78410276Ssam } 78510276Ssam } 78610276Ssam 78710276Ssam upper(s) 78810276Ssam char *s; 78910276Ssam { 79010276Ssam while (*s != '\0') { 79110276Ssam if (islower(*s)) 79210276Ssam *s = toupper(*s); 79310276Ssam s++; 79410276Ssam } 79510276Ssam } 79610276Ssam 79710276Ssam copy(s) 79810276Ssam char *s; 79910276Ssam { 80010276Ssam char *p; 80126494Sminshall extern char *malloc(), *strcpy(); 80210276Ssam 80326494Sminshall p = malloc((unsigned) strlen(s) + 1); 80410276Ssam if (p == NULL) 80510276Ssam fatal("Ran out of memory."); 80626494Sminshall (void) strcpy(p, s); 80710276Ssam return ((int)p); 80810276Ssam } 80910276Ssam 81010276Ssam help(s) 81110276Ssam char *s; 81210276Ssam { 81310276Ssam register struct tab *c; 81410276Ssam register int width, NCMDS; 81510276Ssam 81610276Ssam width = 0, NCMDS = 0; 81710276Ssam for (c = cmdtab; c->name != NULL; c++) { 81810276Ssam int len = strlen(c->name); 81910276Ssam 82010276Ssam if (c->implemented == 0) 82110276Ssam len++; 82210276Ssam if (len > width) 82310276Ssam width = len; 82410276Ssam NCMDS++; 82510276Ssam } 82610276Ssam width = (width + 8) &~ 7; 82710276Ssam if (s == 0) { 82810276Ssam register int i, j, w; 82910276Ssam int columns, lines; 83010276Ssam 83110276Ssam lreply(214, 83210276Ssam "The following commands are recognized (* =>'s unimplemented)."); 83310276Ssam columns = 76 / width; 83410276Ssam if (columns == 0) 83510276Ssam columns = 1; 83610276Ssam lines = (NCMDS + columns - 1) / columns; 83710276Ssam for (i = 0; i < lines; i++) { 838*27107Smckusick printf(" "); 83910276Ssam for (j = 0; j < columns; j++) { 84010276Ssam c = cmdtab + j * lines + i; 84110276Ssam printf("%s%c", c->name, 84210276Ssam c->implemented ? ' ' : '*'); 84310302Ssam if (c + lines >= &cmdtab[NCMDS]) 84410276Ssam break; 84510276Ssam w = strlen(c->name); 84610276Ssam while (w < width) { 84710276Ssam putchar(' '); 84810276Ssam w++; 84910276Ssam } 85010276Ssam } 85110276Ssam printf("\r\n"); 85210276Ssam } 85326494Sminshall (void) fflush(stdout); 85410276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 85510276Ssam return; 85610276Ssam } 85710276Ssam upper(s); 85810276Ssam c = lookup(s); 85910276Ssam if (c == (struct tab *)0) { 860*27107Smckusick reply(502, "Unknown command %s.", s); 86110276Ssam return; 86210276Ssam } 86310276Ssam if (c->implemented) 86410276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 86510276Ssam else 86610276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 86710276Ssam } 868