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