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