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