1/* 2 * Copyright (c) 1985, 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 * 17 * @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89 18 */ 19 20/* 21 * Grammar for FTP commands. 22 * See RFC 959. 23 */ 24 25%{ 26 27/* sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; */ 28 29#include <sys/param.h> 30#include <sys/socket.h> 31 32#include <netinet/in.h> 33 34#include <arpa/ftp.h> 35 36#include <stdlib.h> 37#include <unistd.h> 38#include <stdio.h> 39#include <signal.h> 40#include <ctype.h> 41#include <pwd.h> 42#include <setjmp.h> 43#include <syslog.h> 44#include <sys/stat.h> 45#include <string.h> 46#include <time.h> 47#include <assert.h> 48 49#ifdef YYBISON 50int yylex(void); 51static void yyerror(const char *); 52#endif 53 54extern struct sockaddr_in data_dest; 55extern int logged_in; 56extern struct passwd *pw; 57extern int guest; 58extern int logging; 59extern int type; 60extern int form; 61extern int debug; 62extern int timeout; 63extern int maxtimeout; 64extern int pdata; 65extern char hostname[], remotehost[]; 66extern char proctitle[]; 67extern char *globerr; 68extern int usedefault; 69extern int transflag; 70extern char tmpline[]; 71 72extern char **glob(char *); 73extern char *renamefrom(char *); 74extern void cwd(const char *); 75 76extern void dologout(int); 77extern void fatal(const char *); 78extern void makedir(const char *); 79extern void nack(const char *); 80extern void pass(const char *); 81extern void passive(void); 82extern void pwd(void); 83extern void removedir(char *); 84extern void renamecmd(char *, char *); 85extern void retrieve(const char *, const char *); 86extern void send_file_list(const char *); 87extern void statcmd(void); 88extern void statfilecmd(const char *); 89extern void store(char *, const char *, int); 90extern void user(const char *); 91 92extern void perror_reply(int, const char *, ...); 93extern void reply(int, const char *, ...); 94extern void lreply(int, const char *, ...); 95 96static int cmd_type; 97static int cmd_form; 98static int cmd_bytesz; 99char cbuf[512]; 100char *fromname; 101 102struct tab { 103 const char *name; 104 short token; 105 short state; 106 short implemented; /* 1 if command is implemented */ 107 const char *help; 108}; 109 110static char * copy(const char *); 111 112#ifdef YYBISON 113static void sizecmd(char *filename); 114static void help(struct tab *ctab, char *s); 115struct tab cmdtab[]; 116struct tab sitetab[]; 117#endif 118 119static void 120yyerror(const char *msg) 121{ 122 perror(msg); 123} 124%} 125 126%union 127{ 128 int ival; 129 char *sval; 130} 131%token <ival> NUMBER 132%token <sval> STRING 133 134%type <ival> 135 byte_size 136 check_login 137 form_code 138 mode_code 139 octal_number 140 struct_code 141 142%type <sval> 143 password 144 pathname 145 pathstring 146 username 147 148%token 149 A B C E F I 150 L N P R S T 151 152 SP CRLF COMMA STRING NUMBER 153 154 USER PASS ACCT REIN QUIT PORT 155 PASV TYPE STRU MODE RETR STOR 156 APPE MLFL MAIL MSND MSOM MSAM 157 MRSQ MRCP ALLO REST RNFR RNTO 158 ABOR DELE CWD LIST NLST SITE 159 STAT HELP NOOP MKD RMD PWD 160 CDUP STOU SMNT SYST SIZE MDTM 161 162 UMASK IDLE CHMOD 163 164 LEXERR 165 166%start cmd_list 167 168%% 169 170cmd_list: /* empty */ 171 | cmd_list cmd 172 { 173 fromname = (char *) 0; 174 } 175 | cmd_list rcmd 176 ; 177 178cmd: USER SP username CRLF 179 { 180 user($3); 181 free($3); 182 } 183 | PASS SP password CRLF 184 { 185 pass($3); 186 free($3); 187 } 188 | PORT SP host_port CRLF 189 { 190 usedefault = 0; 191 if (pdata >= 0) { 192 (void) close(pdata); 193 pdata = -1; 194 } 195 reply(200, "PORT command successful."); 196 } 197 | PASV CRLF 198 { 199 passive(); 200 } 201 | TYPE SP type_code CRLF 202 { 203 switch (cmd_type) { 204 205 case TYPE_A: 206 if (cmd_form == FORM_N) { 207 reply(200, "Type set to A."); 208 type = cmd_type; 209 form = cmd_form; 210 } else 211 reply(504, "Form must be N."); 212 break; 213 214 case TYPE_E: 215 reply(504, "Type E not implemented."); 216 break; 217 218 case TYPE_I: 219 reply(200, "Type set to I."); 220 type = cmd_type; 221 break; 222 223 case TYPE_L: 224#if NBBY == 8 225 if (cmd_bytesz == 8) { 226 reply(200, 227 "Type set to L (byte size 8)."); 228 type = cmd_type; 229 } else 230 reply(504, "Byte size must be 8."); 231#else /* NBBY == 8 */ 232 UNIMPLEMENTED for NBBY != 8 233#endif /* NBBY == 8 */ 234 } 235 } 236 | STRU SP struct_code CRLF 237 { 238 switch ($3) { 239 240 case STRU_F: 241 reply(200, "STRU F ok."); 242 break; 243 244 default: 245 reply(504, "Unimplemented STRU type."); 246 } 247 } 248 | MODE SP mode_code CRLF 249 { 250 switch ($3) { 251 252 case MODE_S: 253 reply(200, "MODE S ok."); 254 break; 255 256 default: 257 reply(502, "Unimplemented MODE type."); 258 } 259 } 260 | ALLO SP NUMBER CRLF 261 { 262 reply(202, "ALLO command ignored."); 263 } 264 | ALLO SP NUMBER SP R SP NUMBER CRLF 265 { 266 reply(202, "ALLO command ignored."); 267 } 268 | RETR check_login SP pathname CRLF 269 { 270 if ($2 && $4 != 0) 271 retrieve((char *) 0, $4); 272 if ($4 != 0) 273 free($4); 274 } 275 | STOR check_login SP pathname CRLF 276 { 277 if ($2 && $4 != 0) 278 store($4, "w", 0); 279 if ($4 != 0) 280 free($4); 281 } 282 | APPE check_login SP pathname CRLF 283 { 284 if ($2 && $4 != 0) 285 store($4, "a", 0); 286 if ($4 != 0) 287 free($4); 288 } 289 | NLST check_login CRLF 290 { 291 if ($2) 292 send_file_list("."); 293 } 294 | NLST check_login SP STRING CRLF 295 { 296 if ($2 && $4 != 0) 297 send_file_list((char *) $4); 298 if ($4 != 0) 299 free((char *) $4); 300 } 301 | LIST check_login CRLF 302 { 303 if ($2) 304 retrieve("/bin/ls -lgA", ""); 305 } 306 | LIST check_login SP pathname CRLF 307 { 308 if ($2 && $4 != 0) 309 retrieve("/bin/ls -lgA %s", $4); 310 if ($4 != 0) 311 free($4); 312 } 313 | STAT check_login SP pathname CRLF 314 { 315 if ($2 && $4 != 0) 316 statfilecmd($4); 317 if ($4 != 0) 318 free($4); 319 } 320 | STAT CRLF 321 { 322 statcmd(); 323 } 324 | DELE check_login SP pathname CRLF 325 { 326 if ($2 && $4 != 0) 327 remove((char *) $4); 328 if ($4 != 0) 329 free((char *) $4); 330 } 331 | RNTO SP pathname CRLF 332 { 333 if (fromname) { 334 renamecmd(fromname, (char *) $3); 335 free(fromname); 336 fromname = (char *) 0; 337 } else { 338 reply(503, "Bad sequence of commands."); 339 } 340 free((char *) $3); 341 } 342 | ABOR CRLF 343 { 344 reply(225, "ABOR command successful."); 345 } 346 | CWD check_login CRLF 347 { 348 if ($2) 349 cwd(pw->pw_dir); 350 } 351 | CWD check_login SP pathname CRLF 352 { 353 if ($2 && $4 != 0) 354 cwd((char *) $4); 355 if ($4 != 0) 356 free((char *) $4); 357 } 358 | HELP CRLF 359 { 360 help(cmdtab, (char *) 0); 361 } 362 | HELP SP STRING CRLF 363 { 364 register char *cp = (char *)$3; 365 366 if (strncasecmp(cp, "SITE", 4) == 0) { 367 cp = (char *)$3 + 4; 368 if (*cp == ' ') 369 cp++; 370 if (*cp) 371 help(sitetab, cp); 372 else 373 help(sitetab, (char *) 0); 374 } else 375 help(cmdtab, (char *) $3); 376 } 377 | NOOP CRLF 378 { 379 reply(200, "NOOP command successful."); 380 } 381 | MKD check_login SP pathname CRLF 382 { 383 if ($2 && $4 != 0) 384 makedir((char *) $4); 385 if ($4 != 0) 386 free((char *) $4); 387 } 388 | RMD check_login SP pathname CRLF 389 { 390 if ($2 && $4 != 0) 391 removedir((char *) $4); 392 if ($4 != 0) 393 free((char *) $4); 394 } 395 | PWD check_login CRLF 396 { 397 if ($2) 398 pwd(); 399 } 400 | CDUP check_login CRLF 401 { 402 if ($2) 403 cwd(".."); 404 } 405 | SITE SP HELP CRLF 406 { 407 help(sitetab, (char *) 0); 408 } 409 | SITE SP HELP SP STRING CRLF 410 { 411 help(sitetab, (char *) $5); 412 } 413 | SITE SP UMASK check_login CRLF 414 { 415 int oldmask; 416 417 if ($4) { 418 oldmask = umask(0); 419 (void) umask(oldmask); 420 reply(200, "Current UMASK is %03o", oldmask); 421 } 422 } 423 | SITE SP UMASK check_login SP octal_number CRLF 424 { 425 int oldmask; 426 427 if ($4) { 428 if (($6 == -1) || ($6 > 0777)) { 429 reply(501, "Bad UMASK value"); 430 } else { 431 oldmask = umask($6); 432 reply(200, 433 "UMASK set to %03o (was %03o)", 434 $6, oldmask); 435 } 436 } 437 } 438 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 439 { 440 if ($4 && ($8 != 0)) { 441 if ($6 > 0777) 442 reply(501, 443 "CHMOD: Mode value must be between 0 and 0777"); 444 else if (chmod((char *) $8, $6) < 0) 445 perror_reply(550, (char *) $8); 446 else 447 reply(200, "CHMOD command successful."); 448 } 449 if ($8 != 0) 450 free((char *) $8); 451 } 452 | SITE SP IDLE CRLF 453 { 454 reply(200, 455 "Current IDLE time limit is %d seconds; max %d", 456 timeout, maxtimeout); 457 } 458 | SITE SP IDLE SP NUMBER CRLF 459 { 460 if ($5 < 30 || $5 > maxtimeout) { 461 reply(501, 462 "Maximum IDLE time must be between 30 and %d seconds", 463 maxtimeout); 464 } else { 465 timeout = $5; 466 (void) alarm((unsigned) timeout); 467 reply(200, 468 "Maximum IDLE time set to %d seconds", 469 timeout); 470 } 471 } 472 | STOU check_login SP pathname CRLF 473 { 474 if ($2 && $4 != 0) 475 store((char *) $4, "w", 1); 476 if ($4 != 0) 477 free((char *) $4); 478 } 479 | SYST CRLF 480 { 481#ifdef unix 482#ifdef BSD 483 reply(215, "UNIX Type: L%d Version: BSD-%d", 484 NBBY, BSD); 485#else /* BSD */ 486 reply(215, "UNIX Type: L%d", NBBY); 487#endif /* BSD */ 488#else /* unix */ 489 reply(215, "UNKNOWN Type: L%d", NBBY); 490#endif /* unix */ 491 } 492 493 /* 494 * SIZE is not in RFC959, but Postel has blessed it and 495 * it will be in the updated RFC. 496 * 497 * Return size of file in a format suitable for 498 * using with RESTART (we just count bytes). 499 */ 500 | SIZE check_login SP pathname CRLF 501 { 502 if ($2 && $4 != 0) 503 sizecmd((char *) $4); 504 if ($4 != 0) 505 free((char *) $4); 506 } 507 508 /* 509 * MDTM is not in RFC959, but Postel has blessed it and 510 * it will be in the updated RFC. 511 * 512 * Return modification time of file as an ISO 3307 513 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 514 * where xxx is the fractional second (of any precision, 515 * not necessarily 3 digits) 516 */ 517 | MDTM check_login SP pathname CRLF 518 { 519 if ($2 && $4 != 0) { 520 struct stat stbuf; 521 if (stat((char *) $4, &stbuf) < 0) 522 perror_reply(550, "%s", (char *) $4); 523 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 524 reply(550, "%s: not a plain file.", 525 (char *) $4); 526 } else { 527 register struct tm *t; 528 t = gmtime(&stbuf.st_mtime); 529 reply(213, 530 "%04d%02d%02d%02d%02d%02d", 531 1900 + t->tm_year, 532 t->tm_mon+1, t->tm_mday, 533 t->tm_hour, t->tm_min, t->tm_sec); 534 } 535 } 536 if ($4 != 0) 537 free((char *) $4); 538 } 539 | QUIT CRLF 540 { 541 reply(221, "Goodbye."); 542 dologout(0); 543 } 544 | error CRLF 545 { 546 yyerrok; 547 } 548 ; 549rcmd: RNFR check_login SP pathname CRLF 550 { 551 if ($2 && $4) { 552 fromname = renamefrom((char *) $4); 553 if (fromname == (char *) 0 && $4) { 554 free((char *) $4); 555 } 556 } 557 } 558 ; 559 560username: STRING 561 ; 562 563password: /* empty */ 564 { 565 *(const char **)(&($$)) = ""; 566 } 567 | STRING 568 ; 569 570byte_size: NUMBER 571 ; 572 573host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 574 NUMBER COMMA NUMBER 575 { 576 register char *a, *p; 577 578 a = (char *)&data_dest.sin_addr; 579 a[0] = (char) $1; 580 a[1] = (char) $3; 581 a[2] = (char) $5; 582 a[3] = (char) $7; 583 p = (char *)&data_dest.sin_port; 584 p[0] = (char) $9; 585 p[1] = (char) $11; 586 data_dest.sin_family = AF_INET; 587 } 588 ; 589 590form_code: N 591 { 592 $$ = FORM_N; 593 } 594 | T 595 { 596 $$ = FORM_T; 597 } 598 | C 599 { 600 $$ = FORM_C; 601 } 602 ; 603 604type_code: A 605 { 606 cmd_type = TYPE_A; 607 cmd_form = FORM_N; 608 } 609 | A SP form_code 610 { 611 cmd_type = TYPE_A; 612 cmd_form = $3; 613 } 614 | E 615 { 616 cmd_type = TYPE_E; 617 cmd_form = FORM_N; 618 } 619 | E SP form_code 620 { 621 cmd_type = TYPE_E; 622 cmd_form = $3; 623 } 624 | I 625 { 626 cmd_type = TYPE_I; 627 } 628 | L 629 { 630 cmd_type = TYPE_L; 631 cmd_bytesz = NBBY; 632 } 633 | L SP byte_size 634 { 635 cmd_type = TYPE_L; 636 cmd_bytesz = $3; 637 } 638 /* this is for a bug in the BBN ftp */ 639 | L byte_size 640 { 641 cmd_type = TYPE_L; 642 cmd_bytesz = $2; 643 } 644 ; 645 646struct_code: F 647 { 648 $$ = STRU_F; 649 } 650 | R 651 { 652 $$ = STRU_R; 653 } 654 | P 655 { 656 $$ = STRU_P; 657 } 658 ; 659 660mode_code: S 661 { 662 $$ = MODE_S; 663 } 664 | B 665 { 666 $$ = MODE_B; 667 } 668 | C 669 { 670 $$ = MODE_C; 671 } 672 ; 673 674pathname: pathstring 675 { 676 /* 677 * Problem: this production is used for all pathname 678 * processing, but only gives a 550 error reply. 679 * This is a valid reply in some cases but not in others. 680 */ 681 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 682 *(char **)&($$) = *glob((char *) $1); 683 if (globerr != 0) { 684 reply(550, globerr); 685 $$ = 0; 686 } 687 free((char *) $1); 688 } else 689 $$ = $1; 690 } 691 ; 692 693pathstring: STRING 694 ; 695 696octal_number: NUMBER 697 { 698 register int ret, dec, multby, digit; 699 700 /* 701 * Convert a number that was read as decimal number 702 * to what it would be if it had been read as octal. 703 */ 704 dec = $1; 705 multby = 1; 706 ret = 0; 707 while (dec) { 708 digit = dec%10; 709 if (digit > 7) { 710 ret = -1; 711 break; 712 } 713 ret += digit * multby; 714 multby *= 8; 715 dec /= 10; 716 } 717 $$ = ret; 718 } 719 ; 720 721check_login: /* empty */ 722 { 723 if (logged_in) 724 $$ = 1; 725 else { 726 reply(530, "Please login with USER and PASS."); 727 $$ = 0; 728 } 729 } 730 ; 731 732%% 733 734#ifdef YYBYACC 735extern int YYLEX_DECL(); 736#endif 737 738extern jmp_buf errcatch; 739 740static void upper(char *); 741 742#define CMD 0 /* beginning of command */ 743#define ARGS 1 /* expect miscellaneous arguments */ 744#define STR1 2 /* expect SP followed by STRING */ 745#define STR2 3 /* expect STRING */ 746#define OSTR 4 /* optional SP then STRING */ 747#define ZSTR1 5 /* SP then optional STRING */ 748#define ZSTR2 6 /* optional STRING after SP */ 749#define SITECMD 7 /* SITE command */ 750#define NSTR 8 /* Number followed by a string */ 751 752struct tab cmdtab[] = { /* In order defined in RFC 765 */ 753 { "USER", USER, STR1, 1, "<sp> username" }, 754 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 755 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 756 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 757 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 758 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 759 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 760 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 761 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 762 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 763 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 764 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 765 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 766 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 767 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 768 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 769 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 770 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 771 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 772 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 773 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 774 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 775 { "REST", REST, ARGS, 0, "(restart command)" }, 776 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 777 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 778 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 779 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 780 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 781 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 782 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 783 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 784 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 785 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 786 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 787 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 788 { "NOOP", NOOP, ARGS, 1, "" }, 789 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 790 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 791 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 792 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 793 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 794 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 795 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 796 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 797 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 798 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 799 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 800 { 0, 0, 0, 0, 0 } 801}; 802 803struct tab sitetab[] = { 804 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 805 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 806 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 807 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 808 { 0, 0, 0, 0, 0 } 809}; 810 811static struct tab * 812lookup(struct tab *p, char *cmd) 813{ 814 815 for (; p->name != 0; p++) 816 if (strcmp(cmd, p->name) == 0) 817 return (p); 818 return (0); 819} 820 821#include <arpa/telnet.h> 822 823/* 824 * get_line - a hacked up version of fgets to ignore TELNET escape codes. 825 */ 826static char * 827get_line(char *s, int n, FILE *iop) 828{ 829 register int c; 830 register char *cs; 831 832 cs = s; 833/* tmpline may contain saved command from urgent mode interruption */ 834 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 835 *cs++ = tmpline[c]; 836 if (tmpline[c] == '\n') { 837 *cs = '\0'; 838 if (debug) 839 syslog(LOG_DEBUG, "command: %s", s); 840 tmpline[0] = '\0'; 841 return(s); 842 } 843 if (c == 0) 844 tmpline[0] = '\0'; 845 } 846 while ((c = getc(iop)) != EOF) { 847 c &= 0377; 848 if (c == IAC) { 849 if ((c = getc(iop)) != EOF) { 850 c &= 0377; 851 switch (c) { 852 case WILL: 853 case WONT: 854 c = getc(iop); 855 printf("%c%c%c", IAC, DONT, 0377&c); 856 (void) fflush(stdout); 857 continue; 858 case DO: 859 case DONT: 860 c = getc(iop); 861 printf("%c%c%c", IAC, WONT, 0377&c); 862 (void) fflush(stdout); 863 continue; 864 case IAC: 865 break; 866 default: 867 continue; /* ignore command */ 868 } 869 } 870 } 871 *cs++ = (char) c; 872 if (--n <= 0 || c == '\n') 873 break; 874 } 875 if (c == EOF && cs == s) 876 return (0); 877 *cs = '\0'; 878 if (debug) 879 syslog(LOG_DEBUG, "command: %s", s); 880 return (s); 881} 882 883static void 884toolong(int sig) 885{ 886 time_t now; 887 888 (void) sig; 889 reply(421, 890 "Timeout (%d seconds): closing control connection.", timeout); 891 (void) time(&now); 892 if (logging) { 893 syslog(LOG_INFO, 894 "User %s timed out after %d seconds at %s", 895 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 896 } 897 dologout(1); 898} 899 900int 901yylex(void) 902{ 903 static int cpos, state; 904 register char *cp, *cp2; 905 register struct tab *p; 906 int n; 907 char c; 908 909 for (;;) { 910 switch (state) { 911 912 case CMD: 913 (void) signal(SIGALRM, toolong); 914 (void) alarm((unsigned) timeout); 915 if (get_line(cbuf, sizeof(cbuf)-1, stdin) == 0) { 916 reply(221, "You could at least say goodbye."); 917 dologout(0); 918 } 919 (void) alarm(0); 920#ifdef SETPROCTITLE 921 if (strncasecmp(cbuf, "PASS", 4) != 0) 922 setproctitle("%s: %s", proctitle, cbuf); 923#endif /* SETPROCTITLE */ 924 if ((cp = strchr(cbuf, '\r'))) { 925 *cp++ = '\n'; 926 *cp = '\0'; 927 } 928 if ((cp = strpbrk(cbuf, " \n"))) 929 cpos = (int) (cp - cbuf); 930 if (cpos == 0) 931 cpos = 4; 932 c = cbuf[cpos]; 933 cbuf[cpos] = '\0'; 934 upper(cbuf); 935 p = lookup(cmdtab, cbuf); 936 cbuf[cpos] = c; 937 if (p != 0) { 938 if (p->implemented == 0) { 939 nack(p->name); 940 longjmp(errcatch,0); 941 /* NOTREACHED */ 942 } 943 state = p->state; 944 *(const char **)(&yylval) = p->name; 945 return (p->token); 946 } 947 break; 948 949 case SITECMD: 950 if (cbuf[cpos] == ' ') { 951 cpos++; 952 return (SP); 953 } 954 cp = &cbuf[cpos]; 955 if ((cp2 = strpbrk(cp, " \n"))) 956 cpos = (int) (cp2 - cbuf); 957 c = cbuf[cpos]; 958 cbuf[cpos] = '\0'; 959 upper(cp); 960 p = lookup(sitetab, cp); 961 cbuf[cpos] = c; 962 if (p != 0) { 963 if (p->implemented == 0) { 964 state = CMD; 965 nack(p->name); 966 longjmp(errcatch,0); 967 /* NOTREACHED */ 968 } 969 state = p->state; 970 *(const char **)(&yylval) = p->name; 971 return (p->token); 972 } 973 state = CMD; 974 break; 975 976 case OSTR: 977 if (cbuf[cpos] == '\n') { 978 state = CMD; 979 return (CRLF); 980 } 981 /* FALLTHROUGH */ 982 983 case STR1: 984 case ZSTR1: 985 dostr1: 986 if (cbuf[cpos] == ' ') { 987 cpos++; 988 if (state == OSTR) 989 state = STR2; 990 else 991 ++state; 992 return (SP); 993 } 994 break; 995 996 case ZSTR2: 997 if (cbuf[cpos] == '\n') { 998 state = CMD; 999 return (CRLF); 1000 } 1001 /* FALLTHROUGH */ 1002 1003 case STR2: 1004 cp = &cbuf[cpos]; 1005 n = (int) strlen(cp); 1006 cpos += n - 1; 1007 /* 1008 * Make sure the string is nonempty and \n terminated. 1009 */ 1010 if (n > 1 && cbuf[cpos] == '\n') { 1011 cbuf[cpos] = '\0'; 1012 *(char **)&yylval = copy(cp); 1013 cbuf[cpos] = '\n'; 1014 state = ARGS; 1015 return (STRING); 1016 } 1017 break; 1018 1019 case NSTR: 1020 if (cbuf[cpos] == ' ') { 1021 cpos++; 1022 return (SP); 1023 } 1024 if (isdigit(cbuf[cpos])) { 1025 cp = &cbuf[cpos]; 1026 while (isdigit(cbuf[++cpos])) 1027 ; 1028 c = cbuf[cpos]; 1029 cbuf[cpos] = '\0'; 1030 yylval.ival = atoi(cp); 1031 cbuf[cpos] = c; 1032 state = STR1; 1033 return (NUMBER); 1034 } 1035 state = STR1; 1036 goto dostr1; 1037 1038 case ARGS: 1039 if (isdigit(cbuf[cpos])) { 1040 cp = &cbuf[cpos]; 1041 while (isdigit(cbuf[++cpos])) 1042 ; 1043 c = cbuf[cpos]; 1044 cbuf[cpos] = '\0'; 1045 yylval.ival = atoi(cp); 1046 cbuf[cpos] = c; 1047 return (NUMBER); 1048 } 1049 switch (cbuf[cpos++]) { 1050 1051 case '\n': 1052 state = CMD; 1053 return (CRLF); 1054 1055 case ' ': 1056 return (SP); 1057 1058 case ',': 1059 return (COMMA); 1060 1061 case 'A': 1062 case 'a': 1063 return (A); 1064 1065 case 'B': 1066 case 'b': 1067 return (B); 1068 1069 case 'C': 1070 case 'c': 1071 return (C); 1072 1073 case 'E': 1074 case 'e': 1075 return (E); 1076 1077 case 'F': 1078 case 'f': 1079 return (F); 1080 1081 case 'I': 1082 case 'i': 1083 return (I); 1084 1085 case 'L': 1086 case 'l': 1087 return (L); 1088 1089 case 'N': 1090 case 'n': 1091 return (N); 1092 1093 case 'P': 1094 case 'p': 1095 return (P); 1096 1097 case 'R': 1098 case 'r': 1099 return (R); 1100 1101 case 'S': 1102 case 's': 1103 return (S); 1104 1105 case 'T': 1106 case 't': 1107 return (T); 1108 1109 } 1110 break; 1111 1112 default: 1113 fatal("Unknown state in scanner."); 1114 } 1115 yyerror((char *) 0); 1116 state = CMD; 1117 longjmp(errcatch,0); 1118 } 1119} 1120 1121static void 1122upper(char *s) 1123{ 1124 while (*s != '\0') { 1125 if (islower(*s)) 1126 *s = toupper(*s); 1127 s++; 1128 } 1129} 1130 1131static char * 1132copy(const char *s) 1133{ 1134 char *p; 1135 1136 p = (char * )malloc(strlen(s) + 1); 1137 if (p == 0) 1138 fatal("Ran out of memory."); 1139 else 1140 (void) strcpy(p, s); 1141 return (p); 1142} 1143 1144static void 1145help(struct tab *ctab, char *s) 1146{ 1147 register struct tab *c; 1148 register int width, NCMDS; 1149 const char *help_type; 1150 1151 if (ctab == sitetab) 1152 help_type = "SITE "; 1153 else 1154 help_type = ""; 1155 width = 0, NCMDS = 0; 1156 for (c = ctab; c->name != 0; c++) { 1157 int len = (int) strlen(c->name); 1158 1159 if (len > width) 1160 width = len; 1161 NCMDS++; 1162 } 1163 width = (width + 8) &~ 7; 1164 if (s == 0) { 1165 register int i, j, w; 1166 int columns, lines; 1167 1168 lreply(214, "The following %scommands are recognized %s.", 1169 help_type, "(* =>'s unimplemented)"); 1170 columns = 76 / width; 1171 if (columns == 0) 1172 columns = 1; 1173 lines = (NCMDS + columns - 1) / columns; 1174 for (i = 0; i < lines; i++) { 1175 printf(" "); 1176 for (j = 0; j < columns; j++) { 1177 c = ctab + j * lines + i; 1178 assert(c->name != 0); 1179 printf("%s%c", c->name, 1180 c->implemented ? ' ' : '*'); 1181 if (c + lines >= &ctab[NCMDS]) 1182 break; 1183 w = (int) strlen(c->name) + 1; 1184 while (w < width) { 1185 putchar(' '); 1186 w++; 1187 } 1188 } 1189 printf("\r\n"); 1190 } 1191 (void) fflush(stdout); 1192 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1193 return; 1194 } 1195 upper(s); 1196 c = lookup(ctab, s); 1197 if (c == (struct tab *)0) { 1198 reply(502, "Unknown command %s.", s); 1199 return; 1200 } 1201 if (c->implemented) 1202 reply(214, "Syntax: %s%s %s", help_type, c->name, c->help); 1203 else 1204 reply(214, "%s%-*s\t%s; unimplemented.", help_type, width, 1205 c->name, c->help); 1206} 1207 1208static void 1209sizecmd(char *filename) 1210{ 1211 switch (type) { 1212 case TYPE_L: 1213 case TYPE_I: { 1214 struct stat stbuf; 1215 if (stat(filename, &stbuf) < 0 || 1216 (stbuf.st_mode&S_IFMT) != S_IFREG) 1217 reply(550, "%s: not a plain file.", filename); 1218 else 1219#ifdef HAVE_LONG_LONG 1220 reply(213, "%llu", (long long) stbuf.st_size); 1221#else 1222 reply(213, "%lu", stbuf.st_size); 1223#endif 1224 break;} 1225 case TYPE_A: { 1226 FILE *fin; 1227 register int c, count; 1228 struct stat stbuf; 1229 fin = fopen(filename, "r"); 1230 if (fin == 0) { 1231 perror_reply(550, filename); 1232 return; 1233 } 1234 if (fstat(fileno(fin), &stbuf) < 0 || 1235 (stbuf.st_mode&S_IFMT) != S_IFREG) { 1236 reply(550, "%s: not a plain file.", filename); 1237 (void) fclose(fin); 1238 return; 1239 } 1240 1241 count = 0; 1242 while((c=getc(fin)) != EOF) { 1243 if (c == '\n') /* will get expanded to \r\n */ 1244 count++; 1245 count++; 1246 } 1247 (void) fclose(fin); 1248 1249 reply(213, "%ld", count); 1250 break;} 1251 default: 1252 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1253 } 1254} 1255