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