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.24 (Berkeley) 02/25/91 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.24 (Berkeley) 02/25/91"; 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 syslog(LOG_DEBUG, "command: %s", s); 813 return (s); 814 } 815 816 static void 817 toolong() 818 { 819 time_t now; 820 821 reply(421, 822 "Timeout (%d seconds): closing control connection.", timeout); 823 (void) time(&now); 824 if (logging) { 825 syslog(LOG_INFO, 826 "User %s timed out after %d seconds at %s", 827 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 828 } 829 dologout(1); 830 } 831 832 yylex() 833 { 834 static int cpos, state; 835 register char *cp, *cp2; 836 register struct tab *p; 837 int n; 838 char c, *copy(); 839 840 for (;;) { 841 switch (state) { 842 843 case CMD: 844 (void) signal(SIGALRM, toolong); 845 (void) alarm((unsigned) timeout); 846 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 847 reply(221, "You could at least say goodbye."); 848 dologout(0); 849 } 850 (void) alarm(0); 851 #ifdef SETPROCTITLE 852 if (strncasecmp(cbuf, "PASS", 4) != NULL) 853 setproctitle("%s: %s", proctitle, cbuf); 854 #endif /* SETPROCTITLE */ 855 if ((cp = index(cbuf, '\r'))) { 856 *cp++ = '\n'; 857 *cp = '\0'; 858 } 859 if ((cp = strpbrk(cbuf, " \n"))) 860 cpos = cp - cbuf; 861 if (cpos == 0) 862 cpos = 4; 863 c = cbuf[cpos]; 864 cbuf[cpos] = '\0'; 865 upper(cbuf); 866 p = lookup(cmdtab, cbuf); 867 cbuf[cpos] = c; 868 if (p != 0) { 869 if (p->implemented == 0) { 870 nack(p->name); 871 longjmp(errcatch,0); 872 /* NOTREACHED */ 873 } 874 state = p->state; 875 *(char **)&yylval = p->name; 876 return (p->token); 877 } 878 break; 879 880 case SITECMD: 881 if (cbuf[cpos] == ' ') { 882 cpos++; 883 return (SP); 884 } 885 cp = &cbuf[cpos]; 886 if ((cp2 = strpbrk(cp, " \n"))) 887 cpos = cp2 - cbuf; 888 c = cbuf[cpos]; 889 cbuf[cpos] = '\0'; 890 upper(cp); 891 p = lookup(sitetab, cp); 892 cbuf[cpos] = c; 893 if (p != 0) { 894 if (p->implemented == 0) { 895 state = CMD; 896 nack(p->name); 897 longjmp(errcatch,0); 898 /* NOTREACHED */ 899 } 900 state = p->state; 901 *(char **)&yylval = p->name; 902 return (p->token); 903 } 904 state = CMD; 905 break; 906 907 case OSTR: 908 if (cbuf[cpos] == '\n') { 909 state = CMD; 910 return (CRLF); 911 } 912 /* FALLTHROUGH */ 913 914 case STR1: 915 case ZSTR1: 916 dostr1: 917 if (cbuf[cpos] == ' ') { 918 cpos++; 919 state = state == OSTR ? STR2 : ++state; 920 return (SP); 921 } 922 break; 923 924 case ZSTR2: 925 if (cbuf[cpos] == '\n') { 926 state = CMD; 927 return (CRLF); 928 } 929 /* FALLTHROUGH */ 930 931 case STR2: 932 cp = &cbuf[cpos]; 933 n = strlen(cp); 934 cpos += n - 1; 935 /* 936 * Make sure the string is nonempty and \n terminated. 937 */ 938 if (n > 1 && cbuf[cpos] == '\n') { 939 cbuf[cpos] = '\0'; 940 *(char **)&yylval = copy(cp); 941 cbuf[cpos] = '\n'; 942 state = ARGS; 943 return (STRING); 944 } 945 break; 946 947 case NSTR: 948 if (cbuf[cpos] == ' ') { 949 cpos++; 950 return (SP); 951 } 952 if (isdigit(cbuf[cpos])) { 953 cp = &cbuf[cpos]; 954 while (isdigit(cbuf[++cpos])) 955 ; 956 c = cbuf[cpos]; 957 cbuf[cpos] = '\0'; 958 yylval = atoi(cp); 959 cbuf[cpos] = c; 960 state = STR1; 961 return (NUMBER); 962 } 963 state = STR1; 964 goto dostr1; 965 966 case ARGS: 967 if (isdigit(cbuf[cpos])) { 968 cp = &cbuf[cpos]; 969 while (isdigit(cbuf[++cpos])) 970 ; 971 c = cbuf[cpos]; 972 cbuf[cpos] = '\0'; 973 yylval = atoi(cp); 974 cbuf[cpos] = c; 975 return (NUMBER); 976 } 977 switch (cbuf[cpos++]) { 978 979 case '\n': 980 state = CMD; 981 return (CRLF); 982 983 case ' ': 984 return (SP); 985 986 case ',': 987 return (COMMA); 988 989 case 'A': 990 case 'a': 991 return (A); 992 993 case 'B': 994 case 'b': 995 return (B); 996 997 case 'C': 998 case 'c': 999 return (C); 1000 1001 case 'E': 1002 case 'e': 1003 return (E); 1004 1005 case 'F': 1006 case 'f': 1007 return (F); 1008 1009 case 'I': 1010 case 'i': 1011 return (I); 1012 1013 case 'L': 1014 case 'l': 1015 return (L); 1016 1017 case 'N': 1018 case 'n': 1019 return (N); 1020 1021 case 'P': 1022 case 'p': 1023 return (P); 1024 1025 case 'R': 1026 case 'r': 1027 return (R); 1028 1029 case 'S': 1030 case 's': 1031 return (S); 1032 1033 case 'T': 1034 case 't': 1035 return (T); 1036 1037 } 1038 break; 1039 1040 default: 1041 fatal("Unknown state in scanner."); 1042 } 1043 yyerror((char *) 0); 1044 state = CMD; 1045 longjmp(errcatch,0); 1046 } 1047 } 1048 1049 upper(s) 1050 register char *s; 1051 { 1052 while (*s != '\0') { 1053 if (islower(*s)) 1054 *s = toupper(*s); 1055 s++; 1056 } 1057 } 1058 1059 char * 1060 copy(s) 1061 char *s; 1062 { 1063 char *p; 1064 1065 p = malloc((unsigned) strlen(s) + 1); 1066 if (p == NULL) 1067 fatal("Ran out of memory."); 1068 (void) strcpy(p, s); 1069 return (p); 1070 } 1071 1072 help(ctab, s) 1073 struct tab *ctab; 1074 char *s; 1075 { 1076 register struct tab *c; 1077 register int width, NCMDS; 1078 char *type; 1079 1080 if (ctab == sitetab) 1081 type = "SITE "; 1082 else 1083 type = ""; 1084 width = 0, NCMDS = 0; 1085 for (c = ctab; c->name != NULL; c++) { 1086 int len = strlen(c->name); 1087 1088 if (len > width) 1089 width = len; 1090 NCMDS++; 1091 } 1092 width = (width + 8) &~ 7; 1093 if (s == 0) { 1094 register int i, j, w; 1095 int columns, lines; 1096 1097 lreply(214, "The following %scommands are recognized %s.", 1098 type, "(* =>'s unimplemented)"); 1099 columns = 76 / width; 1100 if (columns == 0) 1101 columns = 1; 1102 lines = (NCMDS + columns - 1) / columns; 1103 for (i = 0; i < lines; i++) { 1104 printf(" "); 1105 for (j = 0; j < columns; j++) { 1106 c = ctab + j * lines + i; 1107 printf("%s%c", c->name, 1108 c->implemented ? ' ' : '*'); 1109 if (c + lines >= &ctab[NCMDS]) 1110 break; 1111 w = strlen(c->name) + 1; 1112 while (w < width) { 1113 putchar(' '); 1114 w++; 1115 } 1116 } 1117 printf("\r\n"); 1118 } 1119 (void) fflush(stdout); 1120 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1121 return; 1122 } 1123 upper(s); 1124 c = lookup(ctab, s); 1125 if (c == (struct tab *)0) { 1126 reply(502, "Unknown command %s.", s); 1127 return; 1128 } 1129 if (c->implemented) 1130 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1131 else 1132 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1133 c->name, c->help); 1134 } 1135 1136 sizecmd(filename) 1137 char *filename; 1138 { 1139 switch (type) { 1140 case TYPE_L: 1141 case TYPE_I: { 1142 struct stat stbuf; 1143 if (stat(filename, &stbuf) < 0 || 1144 (stbuf.st_mode&S_IFMT) != S_IFREG) 1145 reply(550, "%s: not a plain file.", filename); 1146 else 1147 reply(213, "%lu", stbuf.st_size); 1148 break;} 1149 case TYPE_A: { 1150 FILE *fin; 1151 register int c; 1152 register long count; 1153 struct stat stbuf; 1154 fin = fopen(filename, "r"); 1155 if (fin == NULL) { 1156 perror_reply(550, filename); 1157 return; 1158 } 1159 if (fstat(fileno(fin), &stbuf) < 0 || 1160 (stbuf.st_mode&S_IFMT) != S_IFREG) { 1161 reply(550, "%s: not a plain file.", filename); 1162 (void) fclose(fin); 1163 return; 1164 } 1165 1166 count = 0; 1167 while((c=getc(fin)) != EOF) { 1168 if (c == '\n') /* will get expanded to \r\n */ 1169 count++; 1170 count++; 1171 } 1172 (void) fclose(fin); 1173 1174 reply(213, "%ld", count); 1175 break;} 1176 default: 1177 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1178 } 1179 } 1180