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