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