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