110276Ssam /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 634769Sbostic * 7*60087Sbostic * @(#)ftpcmd.y 5.28 (Berkeley) 05/17/93 822501Sdist */ 922501Sdist 1022501Sdist /* 1110276Ssam * Grammar for FTP commands. 1236933Skarels * See RFC 959. 1310276Ssam */ 1410276Ssam 1510276Ssam %{ 1610276Ssam 1710276Ssam #ifndef lint 18*60087Sbostic static char sccsid[] = "@(#)ftpcmd.y 5.28 (Berkeley) 05/17/93"; 1933738Sbostic #endif /* not lint */ 2010276Ssam 2136552Sbostic #include <sys/param.h> 2210276Ssam #include <sys/socket.h> 2346669Sbostic #include <sys/stat.h> 2454528Sandrew 2510276Ssam #include <netinet/in.h> 2613033Ssam #include <arpa/ftp.h> 2754528Sandrew 2811652Ssam #include <signal.h> 2910276Ssam #include <setjmp.h> 3026494Sminshall #include <syslog.h> 3136933Skarels #include <time.h> 3246669Sbostic #include <pwd.h> 3354528Sandrew #include <errno.h> 3446669Sbostic #include <unistd.h> 3546669Sbostic #include <stdio.h> 3646669Sbostic #include <ctype.h> 3746669Sbostic #include <stdlib.h> 3846669Sbostic #include <string.h> 3954528Sandrew #include "extern.h" 4010276Ssam 4110276Ssam extern struct sockaddr_in data_dest; 4210276Ssam extern int logged_in; 4310276Ssam extern struct passwd *pw; 4410276Ssam extern int guest; 4510276Ssam extern int logging; 4610276Ssam extern int type; 4710276Ssam extern int form; 4810276Ssam extern int debug; 4911652Ssam extern int timeout; 5036933Skarels extern int maxtimeout; 5126045Sminshall extern int pdata; 5236620Srick extern char hostname[], remotehost[]; 5336933Skarels extern char proctitle[]; 5410276Ssam extern char *globerr; 5510320Ssam extern int usedefault; 5626045Sminshall extern int transflag; 5726045Sminshall extern char tmpline[]; 5810276Ssam 5938135Srick off_t restart_point; 6038135Srick 6110276Ssam static int cmd_type; 6210276Ssam static int cmd_form; 6310276Ssam static int cmd_bytesz; 6436304Skarels char cbuf[512]; 6536304Skarels char *fromname; 6610276Ssam 6710276Ssam %} 6810276Ssam 6910276Ssam %token 7010276Ssam A B C E F I 7110276Ssam L N P R S T 7210276Ssam 7310276Ssam SP CRLF COMMA STRING NUMBER 7410276Ssam 7510276Ssam USER PASS ACCT REIN QUIT PORT 7610276Ssam PASV TYPE STRU MODE RETR STOR 7710276Ssam APPE MLFL MAIL MSND MSOM MSAM 7810276Ssam MRSQ MRCP ALLO REST RNFR RNTO 7910276Ssam ABOR DELE CWD LIST NLST SITE 8036620Srick STAT HELP NOOP MKD RMD PWD 8136933Skarels CDUP STOU SMNT SYST SIZE MDTM 8210276Ssam 8336933Skarels UMASK IDLE CHMOD 8436933Skarels 8510276Ssam LEXERR 8610276Ssam 8710276Ssam %start cmd_list 8810276Ssam 8910276Ssam %% 9010276Ssam 9110276Ssam cmd_list: /* empty */ 9210276Ssam | cmd_list cmd 9330945Scsvsj = { 9430945Scsvsj fromname = (char *) 0; 9538135Srick restart_point = (off_t) 0; 9630945Scsvsj } 9730945Scsvsj | cmd_list rcmd 9810276Ssam ; 9910276Ssam 10010276Ssam cmd: USER SP username CRLF 10110276Ssam = { 10236304Skarels user((char *) $3); 10326494Sminshall free((char *) $3); 10410276Ssam } 10510276Ssam | PASS SP password CRLF 10610276Ssam = { 10726494Sminshall pass((char *) $3); 10826494Sminshall free((char *) $3); 10910276Ssam } 11010276Ssam | PORT SP host_port CRLF 11110276Ssam = { 11210320Ssam usedefault = 0; 11336304Skarels if (pdata >= 0) { 11426045Sminshall (void) close(pdata); 11536304Skarels pdata = -1; 11626045Sminshall } 11727107Smckusick reply(200, "PORT command successful."); 11810276Ssam } 11926045Sminshall | PASV CRLF 12026045Sminshall = { 12126045Sminshall passive(); 12226045Sminshall } 12310276Ssam | TYPE SP type_code CRLF 12410276Ssam = { 12510276Ssam switch (cmd_type) { 12610276Ssam 12710276Ssam case TYPE_A: 12810276Ssam if (cmd_form == FORM_N) { 12910276Ssam reply(200, "Type set to A."); 13010276Ssam type = cmd_type; 13110276Ssam form = cmd_form; 13210276Ssam } else 13310276Ssam reply(504, "Form must be N."); 13410276Ssam break; 13510276Ssam 13610276Ssam case TYPE_E: 13710276Ssam reply(504, "Type E not implemented."); 13810276Ssam break; 13910276Ssam 14010276Ssam case TYPE_I: 14110276Ssam reply(200, "Type set to I."); 14210276Ssam type = cmd_type; 14310276Ssam break; 14410276Ssam 14510276Ssam case TYPE_L: 14636933Skarels #if NBBY == 8 14736933Skarels if (cmd_bytesz == 8) { 14810276Ssam reply(200, 14936933Skarels "Type set to L (byte size 8)."); 15010276Ssam type = cmd_type; 15110276Ssam } else 15236933Skarels reply(504, "Byte size must be 8."); 15336933Skarels #else /* NBBY == 8 */ 15436933Skarels UNIMPLEMENTED for NBBY != 8 15536933Skarels #endif /* NBBY == 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: 16727107Smckusick 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 = { 18427107Smckusick reply(202, "ALLO command ignored."); 18510276Ssam } 18636933Skarels | ALLO SP NUMBER SP R SP NUMBER CRLF 18736933Skarels = { 18836933Skarels reply(202, "ALLO command ignored."); 18936933Skarels } 19010276Ssam | RETR check_login SP pathname CRLF 19110276Ssam = { 19210302Ssam if ($2 && $4 != NULL) 19336933Skarels retrieve((char *) 0, (char *) $4); 19410302Ssam if ($4 != NULL) 19526494Sminshall free((char *) $4); 19610276Ssam } 19710276Ssam | STOR check_login SP pathname CRLF 19810276Ssam = { 19910302Ssam if ($2 && $4 != NULL) 20036304Skarels store((char *) $4, "w", 0); 20110302Ssam if ($4 != NULL) 20226494Sminshall free((char *) $4); 20310276Ssam } 20410276Ssam | APPE check_login SP pathname CRLF 20510276Ssam = { 20610302Ssam if ($2 && $4 != NULL) 20736304Skarels store((char *) $4, "a", 0); 20810302Ssam if ($4 != NULL) 20926494Sminshall free((char *) $4); 21010276Ssam } 21110276Ssam | NLST check_login CRLF 21210276Ssam = { 21310276Ssam if ($2) 21436620Srick send_file_list("."); 21510276Ssam } 21636620Srick | NLST check_login SP STRING CRLF 21710276Ssam = { 21852999Sbostic if ($2 && $4 != NULL) 21936620Srick send_file_list((char *) $4); 22010302Ssam if ($4 != NULL) 22126494Sminshall free((char *) $4); 22210276Ssam } 22310276Ssam | LIST check_login CRLF 22410276Ssam = { 22510276Ssam if ($2) 22636620Srick retrieve("/bin/ls -lgA", ""); 22710276Ssam } 22810276Ssam | LIST check_login SP pathname CRLF 22910276Ssam = { 23010302Ssam if ($2 && $4 != NULL) 23136620Srick retrieve("/bin/ls -lgA %s", (char *) $4); 23210302Ssam if ($4 != NULL) 23326494Sminshall free((char *) $4); 23410276Ssam } 23536933Skarels | STAT check_login SP pathname CRLF 23636933Skarels = { 23736933Skarels if ($2 && $4 != NULL) 23836933Skarels statfilecmd((char *) $4); 23936933Skarels if ($4 != NULL) 24036933Skarels free((char *) $4); 24136933Skarels } 24236933Skarels | STAT CRLF 24336933Skarels = { 24436933Skarels statcmd(); 24536933Skarels } 24610276Ssam | DELE check_login SP pathname CRLF 24710276Ssam = { 24810302Ssam if ($2 && $4 != NULL) 24926494Sminshall delete((char *) $4); 25010302Ssam if ($4 != NULL) 25126494Sminshall free((char *) $4); 25210276Ssam } 25330945Scsvsj | RNTO SP pathname CRLF 25430945Scsvsj = { 25530945Scsvsj if (fromname) { 25630945Scsvsj renamecmd(fromname, (char *) $3); 25730945Scsvsj free(fromname); 25830945Scsvsj fromname = (char *) 0; 25930945Scsvsj } else { 26030945Scsvsj reply(503, "Bad sequence of commands."); 26130945Scsvsj } 26230945Scsvsj free((char *) $3); 26330945Scsvsj } 26426045Sminshall | ABOR CRLF 26526045Sminshall = { 26627107Smckusick reply(225, "ABOR command successful."); 26726045Sminshall } 26810276Ssam | CWD check_login CRLF 26910276Ssam = { 27010276Ssam if ($2) 27110276Ssam cwd(pw->pw_dir); 27210276Ssam } 27310276Ssam | CWD check_login SP pathname CRLF 27410276Ssam = { 27510302Ssam if ($2 && $4 != NULL) 27626494Sminshall cwd((char *) $4); 27710302Ssam if ($4 != NULL) 27826494Sminshall free((char *) $4); 27910276Ssam } 28010276Ssam | HELP CRLF 28110276Ssam = { 28236933Skarels help(cmdtab, (char *) 0); 28310276Ssam } 28410276Ssam | HELP SP STRING CRLF 28510276Ssam = { 28636933Skarels register char *cp = (char *)$3; 28736933Skarels 28836933Skarels if (strncasecmp(cp, "SITE", 4) == 0) { 28936933Skarels cp = (char *)$3 + 4; 29036933Skarels if (*cp == ' ') 29136933Skarels cp++; 29236933Skarels if (*cp) 29336933Skarels help(sitetab, cp); 29436933Skarels else 29536933Skarels help(sitetab, (char *) 0); 29636933Skarels } else 29736933Skarels help(cmdtab, (char *) $3); 29810276Ssam } 29910276Ssam | NOOP CRLF 30010276Ssam = { 30127107Smckusick reply(200, "NOOP command successful."); 30210276Ssam } 30336620Srick | MKD check_login SP pathname CRLF 30410276Ssam = { 30510302Ssam if ($2 && $4 != NULL) 30626494Sminshall makedir((char *) $4); 30710302Ssam if ($4 != NULL) 30826494Sminshall free((char *) $4); 30910276Ssam } 31036620Srick | RMD check_login SP pathname CRLF 31110276Ssam = { 31210302Ssam if ($2 && $4 != NULL) 31326494Sminshall removedir((char *) $4); 31410302Ssam if ($4 != NULL) 31526494Sminshall free((char *) $4); 31610276Ssam } 31736620Srick | PWD check_login CRLF 31810276Ssam = { 31910276Ssam if ($2) 32010302Ssam pwd(); 32110276Ssam } 32236620Srick | CDUP check_login CRLF 32310276Ssam = { 32410276Ssam if ($2) 32510276Ssam cwd(".."); 32610276Ssam } 32736933Skarels | SITE SP HELP CRLF 32836933Skarels = { 32936933Skarels help(sitetab, (char *) 0); 33036933Skarels } 33136933Skarels | SITE SP HELP SP STRING CRLF 33236933Skarels = { 33336933Skarels help(sitetab, (char *) $5); 33436933Skarels } 33536933Skarels | SITE SP UMASK check_login CRLF 33636933Skarels = { 33736933Skarels int oldmask; 33836933Skarels 33936933Skarels if ($4) { 34036933Skarels oldmask = umask(0); 34136933Skarels (void) umask(oldmask); 34236933Skarels reply(200, "Current UMASK is %03o", oldmask); 34336933Skarels } 34436933Skarels } 34536933Skarels | SITE SP UMASK check_login SP octal_number CRLF 34636933Skarels = { 34736933Skarels int oldmask; 34836933Skarels 34936933Skarels if ($4) { 35036933Skarels if (($6 == -1) || ($6 > 0777)) { 35136933Skarels reply(501, "Bad UMASK value"); 35236933Skarels } else { 35336933Skarels oldmask = umask($6); 35436933Skarels reply(200, 35536933Skarels "UMASK set to %03o (was %03o)", 35636933Skarels $6, oldmask); 35736933Skarels } 35836933Skarels } 35936933Skarels } 36036933Skarels | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 36136933Skarels = { 36236933Skarels if ($4 && ($8 != NULL)) { 36336933Skarels if ($6 > 0777) 36436933Skarels reply(501, 36536933Skarels "CHMOD: Mode value must be between 0 and 0777"); 36636933Skarels else if (chmod((char *) $8, $6) < 0) 36736933Skarels perror_reply(550, (char *) $8); 36836933Skarels else 36936933Skarels reply(200, "CHMOD command successful."); 37036933Skarels } 37136933Skarels if ($8 != NULL) 37236933Skarels free((char *) $8); 37336933Skarels } 37436933Skarels | SITE SP IDLE CRLF 37536933Skarels = { 37636933Skarels reply(200, 37736933Skarels "Current IDLE time limit is %d seconds; max %d", 37836933Skarels timeout, maxtimeout); 37936933Skarels } 38036933Skarels | SITE SP IDLE SP NUMBER CRLF 38136933Skarels = { 38236933Skarels if ($5 < 30 || $5 > maxtimeout) { 38336933Skarels reply(501, 38436933Skarels "Maximum IDLE time must be between 30 and %d seconds", 38536933Skarels maxtimeout); 38636933Skarels } else { 38736933Skarels timeout = $5; 38836933Skarels (void) alarm((unsigned) timeout); 38936933Skarels reply(200, 39036933Skarels "Maximum IDLE time set to %d seconds", 39136933Skarels timeout); 39236933Skarels } 39336933Skarels } 39426045Sminshall | STOU check_login SP pathname CRLF 39526045Sminshall = { 39636304Skarels if ($2 && $4 != NULL) 39736304Skarels store((char *) $4, "w", 1); 39826045Sminshall if ($4 != NULL) 39926494Sminshall free((char *) $4); 40026045Sminshall } 40136552Sbostic | SYST CRLF 40236552Sbostic = { 40336640Srick #ifdef unix 40436933Skarels #ifdef BSD 40536552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 40636552Sbostic NBBY, BSD); 40736933Skarels #else /* BSD */ 40836933Skarels reply(215, "UNIX Type: L%d", NBBY); 40936933Skarels #endif /* BSD */ 41036933Skarels #else /* unix */ 41136933Skarels reply(215, "UNKNOWN Type: L%d", NBBY); 41236933Skarels #endif /* unix */ 41336552Sbostic } 41436933Skarels 41536933Skarels /* 41636933Skarels * SIZE is not in RFC959, but Postel has blessed it and 41736933Skarels * it will be in the updated RFC. 41836933Skarels * 41936933Skarels * Return size of file in a format suitable for 42036933Skarels * using with RESTART (we just count bytes). 42136933Skarels */ 42236933Skarels | SIZE check_login SP pathname CRLF 42336933Skarels = { 42436933Skarels if ($2 && $4 != NULL) 42536933Skarels sizecmd((char *) $4); 42636933Skarels if ($4 != NULL) 42736933Skarels free((char *) $4); 42836933Skarels } 42936933Skarels 43036933Skarels /* 43136933Skarels * MDTM is not in RFC959, but Postel has blessed it and 43236933Skarels * it will be in the updated RFC. 43336933Skarels * 43436933Skarels * Return modification time of file as an ISO 3307 43536933Skarels * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 43636933Skarels * where xxx is the fractional second (of any precision, 43736933Skarels * not necessarily 3 digits) 43836933Skarels */ 43936933Skarels | MDTM check_login SP pathname CRLF 44036933Skarels = { 44136933Skarels if ($2 && $4 != NULL) { 44236933Skarels struct stat stbuf; 44336933Skarels if (stat((char *) $4, &stbuf) < 0) 44454528Sandrew reply(550, "%s: %s", 44554528Sandrew (char *)$4, strerror(errno)); 44636933Skarels else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 44736933Skarels reply(550, "%s: not a plain file.", 44836933Skarels (char *) $4); 44936933Skarels } else { 45036933Skarels register struct tm *t; 45136933Skarels t = gmtime(&stbuf.st_mtime); 45236933Skarels reply(213, 45336933Skarels "19%02d%02d%02d%02d%02d%02d", 45436933Skarels t->tm_year, t->tm_mon+1, t->tm_mday, 45536933Skarels t->tm_hour, t->tm_min, t->tm_sec); 45636933Skarels } 45736933Skarels } 45836933Skarels if ($4 != NULL) 45936933Skarels free((char *) $4); 46036933Skarels } 46110276Ssam | QUIT CRLF 46210276Ssam = { 46310276Ssam reply(221, "Goodbye."); 46413246Ssam dologout(0); 46510276Ssam } 46610276Ssam | error CRLF 46710276Ssam = { 46810276Ssam yyerrok; 46910276Ssam } 47010276Ssam ; 47130945Scsvsj rcmd: RNFR check_login SP pathname CRLF 47230945Scsvsj = { 47330945Scsvsj char *renamefrom(); 47430945Scsvsj 47538135Srick restart_point = (off_t) 0; 47630945Scsvsj if ($2 && $4) { 47730945Scsvsj fromname = renamefrom((char *) $4); 47830945Scsvsj if (fromname == (char *) 0 && $4) { 47930945Scsvsj free((char *) $4); 48030945Scsvsj } 48130945Scsvsj } 48230945Scsvsj } 48338135Srick | REST SP byte_size CRLF 48438135Srick = { 48538135Srick fromname = (char *) 0; 48638135Srick restart_point = $3; 48738135Srick reply(350, "Restarting at %ld. %s", restart_point, 48838135Srick "Send STORE or RETRIEVE to initiate transfer."); 48938135Srick } 49030945Scsvsj ; 49152999Sbostic 49210276Ssam username: STRING 49310276Ssam ; 49410276Ssam 49536304Skarels password: /* empty */ 49636304Skarels = { 49740184Smckusick *(char **)&($$) = (char *)calloc(1, sizeof(char)); 49836304Skarels } 49936304Skarels | STRING 50010276Ssam ; 50110276Ssam 50210276Ssam byte_size: NUMBER 50310276Ssam ; 50410276Ssam 50552999Sbostic host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 50610276Ssam NUMBER COMMA NUMBER 50710276Ssam = { 50810276Ssam register char *a, *p; 50910276Ssam 51010276Ssam a = (char *)&data_dest.sin_addr; 51110276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 51210276Ssam p = (char *)&data_dest.sin_port; 51310276Ssam p[0] = $9; p[1] = $11; 51410324Ssam data_dest.sin_family = AF_INET; 51510276Ssam } 51610276Ssam ; 51710276Ssam 51810276Ssam form_code: N 51910276Ssam = { 52010276Ssam $$ = FORM_N; 52110276Ssam } 52210276Ssam | T 52310276Ssam = { 52410276Ssam $$ = FORM_T; 52510276Ssam } 52610276Ssam | C 52710276Ssam = { 52810276Ssam $$ = FORM_C; 52910276Ssam } 53010276Ssam ; 53110276Ssam 53210276Ssam type_code: A 53310276Ssam = { 53410276Ssam cmd_type = TYPE_A; 53510276Ssam cmd_form = FORM_N; 53610276Ssam } 53710276Ssam | A SP form_code 53810276Ssam = { 53910276Ssam cmd_type = TYPE_A; 54010276Ssam cmd_form = $3; 54110276Ssam } 54210276Ssam | E 54310276Ssam = { 54410276Ssam cmd_type = TYPE_E; 54510276Ssam cmd_form = FORM_N; 54610276Ssam } 54710276Ssam | E SP form_code 54810276Ssam = { 54910276Ssam cmd_type = TYPE_E; 55010276Ssam cmd_form = $3; 55110276Ssam } 55210276Ssam | I 55310276Ssam = { 55410276Ssam cmd_type = TYPE_I; 55510276Ssam } 55610276Ssam | L 55710276Ssam = { 55810276Ssam cmd_type = TYPE_L; 55936552Sbostic cmd_bytesz = NBBY; 56010276Ssam } 56110276Ssam | L SP byte_size 56210276Ssam = { 56310276Ssam cmd_type = TYPE_L; 56410276Ssam cmd_bytesz = $3; 56510276Ssam } 56610276Ssam /* this is for a bug in the BBN ftp */ 56710276Ssam | L byte_size 56810276Ssam = { 56910276Ssam cmd_type = TYPE_L; 57010276Ssam cmd_bytesz = $2; 57110276Ssam } 57210276Ssam ; 57310276Ssam 57410276Ssam struct_code: F 57510276Ssam = { 57610276Ssam $$ = STRU_F; 57710276Ssam } 57810276Ssam | R 57910276Ssam = { 58010276Ssam $$ = STRU_R; 58110276Ssam } 58210276Ssam | P 58310276Ssam = { 58410276Ssam $$ = STRU_P; 58510276Ssam } 58610276Ssam ; 58710276Ssam 58810276Ssam mode_code: S 58910276Ssam = { 59010276Ssam $$ = MODE_S; 59110276Ssam } 59210276Ssam | B 59310276Ssam = { 59410276Ssam $$ = MODE_B; 59510276Ssam } 59610276Ssam | C 59710276Ssam = { 59810276Ssam $$ = MODE_C; 59910276Ssam } 60010276Ssam ; 60110276Ssam 60210276Ssam pathname: pathstring 60310276Ssam = { 60427107Smckusick /* 60527107Smckusick * Problem: this production is used for all pathname 60627107Smckusick * processing, but only gives a 550 error reply. 60727107Smckusick * This is a valid reply in some cases but not in others. 60827107Smckusick */ 60936304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 61046669Sbostic *(char **)&($$) = *ftpglob((char *) $1); 61110302Ssam if (globerr != NULL) { 61210276Ssam reply(550, globerr); 61310302Ssam $$ = NULL; 61410302Ssam } 61526494Sminshall free((char *) $1); 61610276Ssam } else 61710276Ssam $$ = $1; 61810276Ssam } 61910276Ssam ; 62010276Ssam 62110276Ssam pathstring: STRING 62210276Ssam ; 62310276Ssam 62436933Skarels octal_number: NUMBER 62536933Skarels = { 62636933Skarels register int ret, dec, multby, digit; 62736933Skarels 62836933Skarels /* 62936933Skarels * Convert a number that was read as decimal number 63036933Skarels * to what it would be if it had been read as octal. 63136933Skarels */ 63236933Skarels dec = $1; 63336933Skarels multby = 1; 63436933Skarels ret = 0; 63536933Skarels while (dec) { 63636933Skarels digit = dec%10; 63736933Skarels if (digit > 7) { 63836933Skarels ret = -1; 63936933Skarels break; 64036933Skarels } 64136933Skarels ret += digit * multby; 64236933Skarels multby *= 8; 64336933Skarels dec /= 10; 64436933Skarels } 64536933Skarels $$ = ret; 64636933Skarels } 64736933Skarels ; 64836933Skarels 64910276Ssam check_login: /* empty */ 65010276Ssam = { 65110276Ssam if (logged_in) 65210276Ssam $$ = 1; 65310276Ssam else { 65410276Ssam reply(530, "Please login with USER and PASS."); 65510276Ssam $$ = 0; 65610276Ssam } 65710276Ssam } 65810276Ssam ; 65910276Ssam 66010276Ssam %% 66110276Ssam 66210276Ssam extern jmp_buf errcatch; 66310276Ssam 66410276Ssam #define CMD 0 /* beginning of command */ 66510276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 66610276Ssam #define STR1 2 /* expect SP followed by STRING */ 66710276Ssam #define STR2 3 /* expect STRING */ 66836304Skarels #define OSTR 4 /* optional SP then STRING */ 66936304Skarels #define ZSTR1 5 /* SP then optional STRING */ 67036304Skarels #define ZSTR2 6 /* optional STRING after SP */ 67136933Skarels #define SITECMD 7 /* SITE command */ 67236933Skarels #define NSTR 8 /* Number followed by a string */ 67310276Ssam 67410276Ssam struct tab { 67510276Ssam char *name; 67610276Ssam short token; 67710276Ssam short state; 67810276Ssam short implemented; /* 1 if command is implemented */ 67910276Ssam char *help; 68010276Ssam }; 68110276Ssam 68210276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 68310276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 68436304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 68510276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 68636933Skarels { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 68710276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 68810276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 68910276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 69026045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 69110276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 69210276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 69310276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 69410276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 69510276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 69610276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 69710276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 69810276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 69910276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 70010276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 70110276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 70210276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 70310276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 70410276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 70554058Sandrew { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 70610276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 70710276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 70826045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 70910276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 71036304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 71110276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 71210276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 71310276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 71436933Skarels { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 71536552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 71636933Skarels { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 71710276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 71810276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 71936620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 72036620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 72136620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 72236620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 72336620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 72436620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 72536620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 72636620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 72726045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 72836933Skarels { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 72936933Skarels { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 73010276Ssam { NULL, 0, 0, 0, 0 } 73110276Ssam }; 73210276Ssam 73336933Skarels struct tab sitetab[] = { 73436933Skarels { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 73536933Skarels { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 73636933Skarels { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 73736933Skarels { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 73836933Skarels { NULL, 0, 0, 0, 0 } 73936933Skarels }; 74036933Skarels 74154528Sandrew static char *copy __P((char *)); 74254528Sandrew static void help __P((struct tab *, char *)); 74354528Sandrew static struct tab * 74454528Sandrew lookup __P((struct tab *, char *)); 74554528Sandrew static void sizecmd __P((char *)); 74654528Sandrew static void toolong __P((int)); 74754528Sandrew static int yylex __P((void)); 74854528Sandrew 74954528Sandrew static struct tab * 75036933Skarels lookup(p, cmd) 75136933Skarels register struct tab *p; 75210276Ssam char *cmd; 75310276Ssam { 75410276Ssam 75536933Skarels for (; p->name != NULL; p++) 75610276Ssam if (strcmp(cmd, p->name) == 0) 75710276Ssam return (p); 75810276Ssam return (0); 75910276Ssam } 76010276Ssam 76113033Ssam #include <arpa/telnet.h> 76210276Ssam 76310276Ssam /* 76410276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 76510276Ssam */ 76610276Ssam char * 76710276Ssam getline(s, n, iop) 76810276Ssam char *s; 76954528Sandrew int n; 77010276Ssam register FILE *iop; 77110276Ssam { 77210276Ssam register c; 77326494Sminshall register char *cs; 77410276Ssam 77510276Ssam cs = s; 77627751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 77726045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 77826045Sminshall *cs++ = tmpline[c]; 77926045Sminshall if (tmpline[c] == '\n') { 78026045Sminshall *cs++ = '\0'; 78136304Skarels if (debug) 78236304Skarels syslog(LOG_DEBUG, "command: %s", s); 78326045Sminshall tmpline[0] = '\0'; 78426045Sminshall return(s); 78526045Sminshall } 78636304Skarels if (c == 0) 78726045Sminshall tmpline[0] = '\0'; 78826045Sminshall } 78936304Skarels while ((c = getc(iop)) != EOF) { 79036304Skarels c &= 0377; 79136304Skarels if (c == IAC) { 79236304Skarels if ((c = getc(iop)) != EOF) { 79336304Skarels c &= 0377; 79436304Skarels switch (c) { 79527751Sminshall case WILL: 79627751Sminshall case WONT: 79736277Sbostic c = getc(iop); 79836304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 79927751Sminshall (void) fflush(stdout); 80036304Skarels continue; 80127751Sminshall case DO: 80227751Sminshall case DONT: 80336277Sbostic c = getc(iop); 80436304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 80527751Sminshall (void) fflush(stdout); 80636304Skarels continue; 80736304Skarels case IAC: 80827751Sminshall break; 80927751Sminshall default: 81036304Skarels continue; /* ignore command */ 81127751Sminshall } 81236304Skarels } 81310276Ssam } 81436304Skarels *cs++ = c; 81536304Skarels if (--n <= 0 || c == '\n') 81610276Ssam break; 81710276Ssam } 81827751Sminshall if (c == EOF && cs == s) 81918303Sralph return (NULL); 82010276Ssam *cs++ = '\0'; 82152999Sbostic if (debug) { 82252999Sbostic if (!guest && strncasecmp("pass ", s, 5) == 0) { 82352999Sbostic /* Don't syslog passwords */ 82452999Sbostic syslog(LOG_DEBUG, "command: %.5s ???", s); 82552999Sbostic } else { 82652999Sbostic register char *cp; 82752999Sbostic register int len; 82852999Sbostic 82952999Sbostic /* Don't syslog trailing CR-LF */ 83052999Sbostic len = strlen(s); 83152999Sbostic cp = s + len - 1; 83252999Sbostic while (cp >= s && (*cp == '\n' || *cp == '\r')) { 83352999Sbostic --cp; 83452999Sbostic --len; 83552999Sbostic } 83652999Sbostic syslog(LOG_DEBUG, "command: %.*s", len, s); 83752999Sbostic } 83852999Sbostic } 83910276Ssam return (s); 84010276Ssam } 84110276Ssam 84246669Sbostic static void 84354528Sandrew toolong(signo) 84454528Sandrew int signo; 84511652Ssam { 84611652Ssam 84711652Ssam reply(421, 84852999Sbostic "Timeout (%d seconds): closing control connection.", timeout); 84952999Sbostic if (logging) 85052999Sbostic syslog(LOG_INFO, "User %s timed out after %d seconds", 85152999Sbostic (pw ? pw -> pw_name : "unknown"), timeout); 85213246Ssam dologout(1); 85311652Ssam } 85411652Ssam 85554528Sandrew static int 85610276Ssam yylex() 85710276Ssam { 85810276Ssam static int cpos, state; 85936933Skarels register char *cp, *cp2; 86010276Ssam register struct tab *p; 86110276Ssam int n; 86254528Sandrew char c; 86310276Ssam 86410276Ssam for (;;) { 86510276Ssam switch (state) { 86610276Ssam 86710276Ssam case CMD: 86826494Sminshall (void) signal(SIGALRM, toolong); 86926494Sminshall (void) alarm((unsigned) timeout); 87010276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 87110276Ssam reply(221, "You could at least say goodbye."); 87213246Ssam dologout(0); 87310276Ssam } 87426494Sminshall (void) alarm(0); 87536620Srick #ifdef SETPROCTITLE 87636933Skarels if (strncasecmp(cbuf, "PASS", 4) != NULL) 87736933Skarels setproctitle("%s: %s", proctitle, cbuf); 87836620Srick #endif /* SETPROCTITLE */ 879*60087Sbostic if ((cp = strchr(cbuf, '\r'))) { 88036324Sbostic *cp++ = '\n'; 88136324Sbostic *cp = '\0'; 88236324Sbostic } 88336277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 88436277Sbostic cpos = cp - cbuf; 88536304Skarels if (cpos == 0) 88610276Ssam cpos = 4; 88710276Ssam c = cbuf[cpos]; 88810276Ssam cbuf[cpos] = '\0'; 88910276Ssam upper(cbuf); 89036933Skarels p = lookup(cmdtab, cbuf); 89110276Ssam cbuf[cpos] = c; 89210276Ssam if (p != 0) { 89310276Ssam if (p->implemented == 0) { 89410276Ssam nack(p->name); 89526494Sminshall longjmp(errcatch,0); 89610276Ssam /* NOTREACHED */ 89710276Ssam } 89810276Ssam state = p->state; 89936933Skarels *(char **)&yylval = p->name; 90010276Ssam return (p->token); 90110276Ssam } 90210276Ssam break; 90310276Ssam 90436933Skarels case SITECMD: 90536933Skarels if (cbuf[cpos] == ' ') { 90636933Skarels cpos++; 90736933Skarels return (SP); 90836933Skarels } 90936933Skarels cp = &cbuf[cpos]; 91036933Skarels if ((cp2 = strpbrk(cp, " \n"))) 91136933Skarels cpos = cp2 - cbuf; 91236933Skarels c = cbuf[cpos]; 91336933Skarels cbuf[cpos] = '\0'; 91436933Skarels upper(cp); 91536933Skarels p = lookup(sitetab, cp); 91636933Skarels cbuf[cpos] = c; 91736933Skarels if (p != 0) { 91836933Skarels if (p->implemented == 0) { 91936933Skarels state = CMD; 92036933Skarels nack(p->name); 92136933Skarels longjmp(errcatch,0); 92236933Skarels /* NOTREACHED */ 92336933Skarels } 92436933Skarels state = p->state; 92536933Skarels *(char **)&yylval = p->name; 92636933Skarels return (p->token); 92736933Skarels } 92836933Skarels state = CMD; 92936933Skarels break; 93036933Skarels 93110276Ssam case OSTR: 93210276Ssam if (cbuf[cpos] == '\n') { 93310276Ssam state = CMD; 93410276Ssam return (CRLF); 93510276Ssam } 93636317Sbostic /* FALLTHROUGH */ 93710276Ssam 93810276Ssam case STR1: 93936304Skarels case ZSTR1: 94036933Skarels dostr1: 94110276Ssam if (cbuf[cpos] == ' ') { 94210276Ssam cpos++; 94336317Sbostic state = state == OSTR ? STR2 : ++state; 94410276Ssam return (SP); 94510276Ssam } 94610276Ssam break; 94710276Ssam 94836304Skarels case ZSTR2: 94936304Skarels if (cbuf[cpos] == '\n') { 95036304Skarels state = CMD; 95136304Skarels return (CRLF); 95236304Skarels } 95336933Skarels /* FALLTHROUGH */ 95436304Skarels 95510276Ssam case STR2: 95610276Ssam cp = &cbuf[cpos]; 95710276Ssam n = strlen(cp); 95810276Ssam cpos += n - 1; 95910276Ssam /* 96010276Ssam * Make sure the string is nonempty and \n terminated. 96110276Ssam */ 96210276Ssam if (n > 1 && cbuf[cpos] == '\n') { 96310276Ssam cbuf[cpos] = '\0'; 96436933Skarels *(char **)&yylval = copy(cp); 96510276Ssam cbuf[cpos] = '\n'; 96610276Ssam state = ARGS; 96710276Ssam return (STRING); 96810276Ssam } 96910276Ssam break; 97010276Ssam 97136933Skarels case NSTR: 97236933Skarels if (cbuf[cpos] == ' ') { 97336933Skarels cpos++; 97436933Skarels return (SP); 97536933Skarels } 97636933Skarels if (isdigit(cbuf[cpos])) { 97736933Skarels cp = &cbuf[cpos]; 97836933Skarels while (isdigit(cbuf[++cpos])) 97936933Skarels ; 98036933Skarels c = cbuf[cpos]; 98136933Skarels cbuf[cpos] = '\0'; 98236933Skarels yylval = atoi(cp); 98336933Skarels cbuf[cpos] = c; 98436933Skarels state = STR1; 98536933Skarels return (NUMBER); 98636933Skarels } 98736933Skarels state = STR1; 98836933Skarels goto dostr1; 98936933Skarels 99010276Ssam case ARGS: 99110276Ssam if (isdigit(cbuf[cpos])) { 99210276Ssam cp = &cbuf[cpos]; 99310276Ssam while (isdigit(cbuf[++cpos])) 99410276Ssam ; 99510276Ssam c = cbuf[cpos]; 99610276Ssam cbuf[cpos] = '\0'; 99710276Ssam yylval = atoi(cp); 99810276Ssam cbuf[cpos] = c; 99910276Ssam return (NUMBER); 100010276Ssam } 100110276Ssam switch (cbuf[cpos++]) { 100210276Ssam 100310276Ssam case '\n': 100410276Ssam state = CMD; 100510276Ssam return (CRLF); 100610276Ssam 100710276Ssam case ' ': 100810276Ssam return (SP); 100910276Ssam 101010276Ssam case ',': 101110276Ssam return (COMMA); 101210276Ssam 101310276Ssam case 'A': 101410276Ssam case 'a': 101510276Ssam return (A); 101610276Ssam 101710276Ssam case 'B': 101810276Ssam case 'b': 101910276Ssam return (B); 102010276Ssam 102110276Ssam case 'C': 102210276Ssam case 'c': 102310276Ssam return (C); 102410276Ssam 102510276Ssam case 'E': 102610276Ssam case 'e': 102710276Ssam return (E); 102810276Ssam 102910276Ssam case 'F': 103010276Ssam case 'f': 103110276Ssam return (F); 103210276Ssam 103310276Ssam case 'I': 103410276Ssam case 'i': 103510276Ssam return (I); 103610276Ssam 103710276Ssam case 'L': 103810276Ssam case 'l': 103910276Ssam return (L); 104010276Ssam 104110276Ssam case 'N': 104210276Ssam case 'n': 104310276Ssam return (N); 104410276Ssam 104510276Ssam case 'P': 104610276Ssam case 'p': 104710276Ssam return (P); 104810276Ssam 104910276Ssam case 'R': 105010276Ssam case 'r': 105110276Ssam return (R); 105210276Ssam 105310276Ssam case 'S': 105410276Ssam case 's': 105510276Ssam return (S); 105610276Ssam 105710276Ssam case 'T': 105810276Ssam case 't': 105910276Ssam return (T); 106010276Ssam 106110276Ssam } 106210276Ssam break; 106310276Ssam 106410276Ssam default: 106510276Ssam fatal("Unknown state in scanner."); 106610276Ssam } 106726494Sminshall yyerror((char *) 0); 106810276Ssam state = CMD; 106926494Sminshall longjmp(errcatch,0); 107010276Ssam } 107110276Ssam } 107210276Ssam 107354528Sandrew void 107410276Ssam upper(s) 107536277Sbostic register char *s; 107610276Ssam { 107710276Ssam while (*s != '\0') { 107810276Ssam if (islower(*s)) 107910276Ssam *s = toupper(*s); 108010276Ssam s++; 108110276Ssam } 108210276Ssam } 108310276Ssam 108454528Sandrew static char * 108510276Ssam copy(s) 108610276Ssam char *s; 108710276Ssam { 108810276Ssam char *p; 108910276Ssam 109026494Sminshall p = malloc((unsigned) strlen(s) + 1); 109110276Ssam if (p == NULL) 109210276Ssam fatal("Ran out of memory."); 109326494Sminshall (void) strcpy(p, s); 109436933Skarels return (p); 109510276Ssam } 109610276Ssam 109754528Sandrew static void 109836933Skarels help(ctab, s) 109936933Skarels struct tab *ctab; 110010276Ssam char *s; 110110276Ssam { 110210276Ssam register struct tab *c; 110310276Ssam register int width, NCMDS; 110436933Skarels char *type; 110510276Ssam 110636933Skarels if (ctab == sitetab) 110736933Skarels type = "SITE "; 110836933Skarels else 110936933Skarels type = ""; 111010276Ssam width = 0, NCMDS = 0; 111136933Skarels for (c = ctab; c->name != NULL; c++) { 111236933Skarels int len = strlen(c->name); 111310276Ssam 111410276Ssam if (len > width) 111510276Ssam width = len; 111610276Ssam NCMDS++; 111710276Ssam } 111810276Ssam width = (width + 8) &~ 7; 111910276Ssam if (s == 0) { 112010276Ssam register int i, j, w; 112110276Ssam int columns, lines; 112210276Ssam 112336933Skarels lreply(214, "The following %scommands are recognized %s.", 112436933Skarels type, "(* =>'s unimplemented)"); 112510276Ssam columns = 76 / width; 112610276Ssam if (columns == 0) 112710276Ssam columns = 1; 112810276Ssam lines = (NCMDS + columns - 1) / columns; 112910276Ssam for (i = 0; i < lines; i++) { 113027107Smckusick printf(" "); 113110276Ssam for (j = 0; j < columns; j++) { 113236933Skarels c = ctab + j * lines + i; 113310276Ssam printf("%s%c", c->name, 113410276Ssam c->implemented ? ' ' : '*'); 113536933Skarels if (c + lines >= &ctab[NCMDS]) 113610276Ssam break; 113731132Smckusick w = strlen(c->name) + 1; 113810276Ssam while (w < width) { 113910276Ssam putchar(' '); 114010276Ssam w++; 114110276Ssam } 114210276Ssam } 114310276Ssam printf("\r\n"); 114410276Ssam } 114526494Sminshall (void) fflush(stdout); 114610276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 114710276Ssam return; 114810276Ssam } 114910276Ssam upper(s); 115036933Skarels c = lookup(ctab, s); 115110276Ssam if (c == (struct tab *)0) { 115227107Smckusick reply(502, "Unknown command %s.", s); 115310276Ssam return; 115410276Ssam } 115510276Ssam if (c->implemented) 115636933Skarels reply(214, "Syntax: %s%s %s", type, c->name, c->help); 115710276Ssam else 115836933Skarels reply(214, "%s%-*s\t%s; unimplemented.", type, width, 115936933Skarels c->name, c->help); 116010276Ssam } 116136933Skarels 116254528Sandrew static void 116336933Skarels sizecmd(filename) 116454528Sandrew char *filename; 116536933Skarels { 116636933Skarels switch (type) { 116736933Skarels case TYPE_L: 116836933Skarels case TYPE_I: { 116936933Skarels struct stat stbuf; 117036933Skarels if (stat(filename, &stbuf) < 0 || 117136933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) 117236933Skarels reply(550, "%s: not a plain file.", filename); 117336933Skarels else 117436933Skarels reply(213, "%lu", stbuf.st_size); 117536933Skarels break;} 117636933Skarels case TYPE_A: { 117736933Skarels FILE *fin; 117838135Srick register int c; 117938135Srick register long count; 118036933Skarels struct stat stbuf; 118136933Skarels fin = fopen(filename, "r"); 118236933Skarels if (fin == NULL) { 118336933Skarels perror_reply(550, filename); 118436933Skarels return; 118536933Skarels } 118636933Skarels if (fstat(fileno(fin), &stbuf) < 0 || 118736933Skarels (stbuf.st_mode&S_IFMT) != S_IFREG) { 118836933Skarels reply(550, "%s: not a plain file.", filename); 118936933Skarels (void) fclose(fin); 119036933Skarels return; 119136933Skarels } 119236933Skarels 119336933Skarels count = 0; 119436933Skarels while((c=getc(fin)) != EOF) { 119536933Skarels if (c == '\n') /* will get expanded to \r\n */ 119636933Skarels count++; 119736933Skarels count++; 119836933Skarels } 119936933Skarels (void) fclose(fin); 120036933Skarels 120136933Skarels reply(213, "%ld", count); 120236933Skarels break;} 120336933Skarels default: 120436933Skarels reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 120536933Skarels } 120636933Skarels } 1207