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