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