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.3 (Berkeley) 04/06/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.3 (Berkeley) 04/06/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; /* XXX $3 is only "int" */ 502 reply(350, "Restarting at %qd. %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 = 636 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 637 638 memset(&gl, 0, sizeof(gl)); 639 if (glob($1, flags, NULL, &gl) || 640 gl.gl_pathc == 0) { 641 reply(550, "not found"); 642 $$ = NULL; 643 } else { 644 $$ = strdup(gl.gl_pathv[0]); 645 } 646 globfree(&gl); 647 free($1); 648 } else 649 $$ = $1; 650 } 651 ; 652 653 pathstring 654 : STRING 655 ; 656 657 octal_number 658 : NUMBER 659 { 660 int ret, dec, multby, digit; 661 662 /* 663 * Convert a number that was read as decimal number 664 * to what it would be if it had been read as octal. 665 */ 666 dec = $1; 667 multby = 1; 668 ret = 0; 669 while (dec) { 670 digit = dec%10; 671 if (digit > 7) { 672 ret = -1; 673 break; 674 } 675 ret += digit * multby; 676 multby *= 8; 677 dec /= 10; 678 } 679 $$ = ret; 680 } 681 ; 682 683 684 check_login 685 : /* empty */ 686 { 687 if (logged_in) 688 $$ = 1; 689 else { 690 reply(530, "Please login with USER and PASS."); 691 $$ = 0; 692 } 693 } 694 ; 695 696 %% 697 698 extern jmp_buf errcatch; 699 700 #define CMD 0 /* beginning of command */ 701 #define ARGS 1 /* expect miscellaneous arguments */ 702 #define STR1 2 /* expect SP followed by STRING */ 703 #define STR2 3 /* expect STRING */ 704 #define OSTR 4 /* optional SP then STRING */ 705 #define ZSTR1 5 /* SP then optional STRING */ 706 #define ZSTR2 6 /* optional STRING after SP */ 707 #define SITECMD 7 /* SITE command */ 708 #define NSTR 8 /* Number followed by a string */ 709 710 struct tab { 711 char *name; 712 short token; 713 short state; 714 short implemented; /* 1 if command is implemented */ 715 char *help; 716 }; 717 718 struct tab cmdtab[] = { /* In order defined in RFC 765 */ 719 { "USER", USER, STR1, 1, "<sp> username" }, 720 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 721 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 722 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 723 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 724 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 725 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 726 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 727 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 728 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 729 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 730 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 731 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 732 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 733 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 734 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 735 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 736 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 737 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 738 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 739 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 740 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 741 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 742 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 743 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 744 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 745 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 746 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 747 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 748 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 749 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 750 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 751 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 752 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 753 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 754 { "NOOP", NOOP, ARGS, 1, "" }, 755 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 756 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 757 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 758 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 759 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 760 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 761 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 762 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 763 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 764 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 765 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 766 { NULL, 0, 0, 0, 0 } 767 }; 768 769 struct tab sitetab[] = { 770 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 771 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 772 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 773 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 774 { NULL, 0, 0, 0, 0 } 775 }; 776 777 static char *copy __P((char *)); 778 static void help __P((struct tab *, char *)); 779 static struct tab * 780 lookup __P((struct tab *, char *)); 781 static void sizecmd __P((char *)); 782 static void toolong __P((int)); 783 static int yylex __P((void)); 784 785 static struct tab * 786 lookup(p, cmd) 787 struct tab *p; 788 char *cmd; 789 { 790 791 for (; p->name != NULL; p++) 792 if (strcmp(cmd, p->name) == 0) 793 return (p); 794 return (0); 795 } 796 797 #include <arpa/telnet.h> 798 799 /* 800 * getline - a hacked up version of fgets to ignore TELNET escape codes. 801 */ 802 char * 803 getline(s, n, iop) 804 char *s; 805 int n; 806 FILE *iop; 807 { 808 int c; 809 register char *cs; 810 811 cs = s; 812 /* tmpline may contain saved command from urgent mode interruption */ 813 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 814 *cs++ = tmpline[c]; 815 if (tmpline[c] == '\n') { 816 *cs++ = '\0'; 817 if (debug) 818 syslog(LOG_DEBUG, "command: %s", s); 819 tmpline[0] = '\0'; 820 return(s); 821 } 822 if (c == 0) 823 tmpline[0] = '\0'; 824 } 825 while ((c = getc(iop)) != EOF) { 826 c &= 0377; 827 if (c == IAC) { 828 if ((c = getc(iop)) != EOF) { 829 c &= 0377; 830 switch (c) { 831 case WILL: 832 case WONT: 833 c = getc(iop); 834 printf("%c%c%c", IAC, DONT, 0377&c); 835 (void) fflush(stdout); 836 continue; 837 case DO: 838 case DONT: 839 c = getc(iop); 840 printf("%c%c%c", IAC, WONT, 0377&c); 841 (void) fflush(stdout); 842 continue; 843 case IAC: 844 break; 845 default: 846 continue; /* ignore command */ 847 } 848 } 849 } 850 *cs++ = c; 851 if (--n <= 0 || c == '\n') 852 break; 853 } 854 if (c == EOF && cs == s) 855 return (NULL); 856 *cs++ = '\0'; 857 if (debug) { 858 if (!guest && strncasecmp("pass ", s, 5) == 0) { 859 /* Don't syslog passwords */ 860 syslog(LOG_DEBUG, "command: %.5s ???", s); 861 } else { 862 register char *cp; 863 register int len; 864 865 /* Don't syslog trailing CR-LF */ 866 len = strlen(s); 867 cp = s + len - 1; 868 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 869 --cp; 870 --len; 871 } 872 syslog(LOG_DEBUG, "command: %.*s", len, s); 873 } 874 } 875 return (s); 876 } 877 878 static void 879 toolong(signo) 880 int signo; 881 { 882 883 reply(421, 884 "Timeout (%d seconds): closing control connection.", timeout); 885 if (logging) 886 syslog(LOG_INFO, "User %s timed out after %d seconds", 887 (pw ? pw -> pw_name : "unknown"), timeout); 888 dologout(1); 889 } 890 891 static int 892 yylex() 893 { 894 static int cpos, state; 895 char *cp, *cp2; 896 struct tab *p; 897 int n; 898 char c; 899 900 for (;;) { 901 switch (state) { 902 903 case CMD: 904 (void) signal(SIGALRM, toolong); 905 (void) alarm((unsigned) timeout); 906 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 907 reply(221, "You could at least say goodbye."); 908 dologout(0); 909 } 910 (void) alarm(0); 911 #ifdef SETPROCTITLE 912 if (strncasecmp(cbuf, "PASS", 4) != NULL) 913 setproctitle("%s: %s", proctitle, cbuf); 914 #endif /* SETPROCTITLE */ 915 if ((cp = strchr(cbuf, '\r'))) { 916 *cp++ = '\n'; 917 *cp = '\0'; 918 } 919 if ((cp = strpbrk(cbuf, " \n"))) 920 cpos = cp - cbuf; 921 if (cpos == 0) 922 cpos = 4; 923 c = cbuf[cpos]; 924 cbuf[cpos] = '\0'; 925 upper(cbuf); 926 p = lookup(cmdtab, cbuf); 927 cbuf[cpos] = c; 928 if (p != 0) { 929 if (p->implemented == 0) { 930 nack(p->name); 931 longjmp(errcatch,0); 932 /* NOTREACHED */ 933 } 934 state = p->state; 935 yylval.s = p->name; 936 return (p->token); 937 } 938 break; 939 940 case SITECMD: 941 if (cbuf[cpos] == ' ') { 942 cpos++; 943 return (SP); 944 } 945 cp = &cbuf[cpos]; 946 if ((cp2 = strpbrk(cp, " \n"))) 947 cpos = cp2 - cbuf; 948 c = cbuf[cpos]; 949 cbuf[cpos] = '\0'; 950 upper(cp); 951 p = lookup(sitetab, cp); 952 cbuf[cpos] = c; 953 if (p != 0) { 954 if (p->implemented == 0) { 955 state = CMD; 956 nack(p->name); 957 longjmp(errcatch,0); 958 /* NOTREACHED */ 959 } 960 state = p->state; 961 yylval.s = p->name; 962 return (p->token); 963 } 964 state = CMD; 965 break; 966 967 case OSTR: 968 if (cbuf[cpos] == '\n') { 969 state = CMD; 970 return (CRLF); 971 } 972 /* FALLTHROUGH */ 973 974 case STR1: 975 case ZSTR1: 976 dostr1: 977 if (cbuf[cpos] == ' ') { 978 cpos++; 979 state = state == OSTR ? STR2 : ++state; 980 return (SP); 981 } 982 break; 983 984 case ZSTR2: 985 if (cbuf[cpos] == '\n') { 986 state = CMD; 987 return (CRLF); 988 } 989 /* FALLTHROUGH */ 990 991 case STR2: 992 cp = &cbuf[cpos]; 993 n = strlen(cp); 994 cpos += n - 1; 995 /* 996 * Make sure the string is nonempty and \n terminated. 997 */ 998 if (n > 1 && cbuf[cpos] == '\n') { 999 cbuf[cpos] = '\0'; 1000 yylval.s = copy(cp); 1001 cbuf[cpos] = '\n'; 1002 state = ARGS; 1003 return (STRING); 1004 } 1005 break; 1006 1007 case NSTR: 1008 if (cbuf[cpos] == ' ') { 1009 cpos++; 1010 return (SP); 1011 } 1012 if (isdigit(cbuf[cpos])) { 1013 cp = &cbuf[cpos]; 1014 while (isdigit(cbuf[++cpos])) 1015 ; 1016 c = cbuf[cpos]; 1017 cbuf[cpos] = '\0'; 1018 yylval.i = atoi(cp); 1019 cbuf[cpos] = c; 1020 state = STR1; 1021 return (NUMBER); 1022 } 1023 state = STR1; 1024 goto dostr1; 1025 1026 case ARGS: 1027 if (isdigit(cbuf[cpos])) { 1028 cp = &cbuf[cpos]; 1029 while (isdigit(cbuf[++cpos])) 1030 ; 1031 c = cbuf[cpos]; 1032 cbuf[cpos] = '\0'; 1033 yylval.i = atoi(cp); 1034 cbuf[cpos] = c; 1035 return (NUMBER); 1036 } 1037 switch (cbuf[cpos++]) { 1038 1039 case '\n': 1040 state = CMD; 1041 return (CRLF); 1042 1043 case ' ': 1044 return (SP); 1045 1046 case ',': 1047 return (COMMA); 1048 1049 case 'A': 1050 case 'a': 1051 return (A); 1052 1053 case 'B': 1054 case 'b': 1055 return (B); 1056 1057 case 'C': 1058 case 'c': 1059 return (C); 1060 1061 case 'E': 1062 case 'e': 1063 return (E); 1064 1065 case 'F': 1066 case 'f': 1067 return (F); 1068 1069 case 'I': 1070 case 'i': 1071 return (I); 1072 1073 case 'L': 1074 case 'l': 1075 return (L); 1076 1077 case 'N': 1078 case 'n': 1079 return (N); 1080 1081 case 'P': 1082 case 'p': 1083 return (P); 1084 1085 case 'R': 1086 case 'r': 1087 return (R); 1088 1089 case 'S': 1090 case 's': 1091 return (S); 1092 1093 case 'T': 1094 case 't': 1095 return (T); 1096 1097 } 1098 break; 1099 1100 default: 1101 fatal("Unknown state in scanner."); 1102 } 1103 yyerror((char *) 0); 1104 state = CMD; 1105 longjmp(errcatch,0); 1106 } 1107 } 1108 1109 void 1110 upper(s) 1111 char *s; 1112 { 1113 while (*s != '\0') { 1114 if (islower(*s)) 1115 *s = toupper(*s); 1116 s++; 1117 } 1118 } 1119 1120 static char * 1121 copy(s) 1122 char *s; 1123 { 1124 char *p; 1125 1126 p = malloc((unsigned) strlen(s) + 1); 1127 if (p == NULL) 1128 fatal("Ran out of memory."); 1129 (void) strcpy(p, s); 1130 return (p); 1131 } 1132 1133 static void 1134 help(ctab, s) 1135 struct tab *ctab; 1136 char *s; 1137 { 1138 struct tab *c; 1139 int width, NCMDS; 1140 char *type; 1141 1142 if (ctab == sitetab) 1143 type = "SITE "; 1144 else 1145 type = ""; 1146 width = 0, NCMDS = 0; 1147 for (c = ctab; c->name != NULL; c++) { 1148 int len = strlen(c->name); 1149 1150 if (len > width) 1151 width = len; 1152 NCMDS++; 1153 } 1154 width = (width + 8) &~ 7; 1155 if (s == 0) { 1156 int i, j, w; 1157 int columns, lines; 1158 1159 lreply(214, "The following %scommands are recognized %s.", 1160 type, "(* =>'s unimplemented)"); 1161 columns = 76 / width; 1162 if (columns == 0) 1163 columns = 1; 1164 lines = (NCMDS + columns - 1) / columns; 1165 for (i = 0; i < lines; i++) { 1166 printf(" "); 1167 for (j = 0; j < columns; j++) { 1168 c = ctab + j * lines + i; 1169 printf("%s%c", c->name, 1170 c->implemented ? ' ' : '*'); 1171 if (c + lines >= &ctab[NCMDS]) 1172 break; 1173 w = strlen(c->name) + 1; 1174 while (w < width) { 1175 putchar(' '); 1176 w++; 1177 } 1178 } 1179 printf("\r\n"); 1180 } 1181 (void) fflush(stdout); 1182 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1183 return; 1184 } 1185 upper(s); 1186 c = lookup(ctab, s); 1187 if (c == (struct tab *)0) { 1188 reply(502, "Unknown command %s.", s); 1189 return; 1190 } 1191 if (c->implemented) 1192 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1193 else 1194 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1195 c->name, c->help); 1196 } 1197 1198 static void 1199 sizecmd(filename) 1200 char *filename; 1201 { 1202 switch (type) { 1203 case TYPE_L: 1204 case TYPE_I: { 1205 struct stat stbuf; 1206 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1207 reply(550, "%s: not a plain file.", filename); 1208 else 1209 reply(213, "%qu", stbuf.st_size); 1210 break; } 1211 case TYPE_A: { 1212 FILE *fin; 1213 int c; 1214 off_t count; 1215 struct stat stbuf; 1216 fin = fopen(filename, "r"); 1217 if (fin == NULL) { 1218 perror_reply(550, filename); 1219 return; 1220 } 1221 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1222 reply(550, "%s: not a plain file.", filename); 1223 (void) fclose(fin); 1224 return; 1225 } 1226 1227 count = 0; 1228 while((c=getc(fin)) != EOF) { 1229 if (c == '\n') /* will get expanded to \r\n */ 1230 count++; 1231 count++; 1232 } 1233 (void) fclose(fin); 1234 1235 reply(213, "%qd", count); 1236 break; } 1237 default: 1238 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1239 } 1240 } 1241