1147072Sbrooks/* $NetBSD: ftp.y,v 1.1.1.4 2011/09/10 21:22:06 christos Exp $ */ 2147072Sbrooks 3147072Sbrooks/* 4147072Sbrooks * Copyright (c) 1985, 1988 Regents of the University of California. 5147072Sbrooks * All rights reserved. 6147072Sbrooks * 7147072Sbrooks * Redistribution and use in source and binary forms are permitted 8147072Sbrooks * provided that the above copyright notice and this paragraph are 9147072Sbrooks * duplicated in all such forms and that any documentation, 10147072Sbrooks * advertising materials, and other materials related to such 11147072Sbrooks * distribution and use acknowledge that the software was developed 12147072Sbrooks * by the University of California, Berkeley. The name of the 13147072Sbrooks * University may not be used to endorse or promote products derived 14147072Sbrooks * from this software without specific prior written permission. 15147072Sbrooks * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16147072Sbrooks * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17147072Sbrooks * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18147072Sbrooks * 19147072Sbrooks * from: @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89 20147072Sbrooks * $NetBSD: ftp.y,v 1.3 2010/12/24 02:58:21 christos Exp $ 21147072Sbrooks */ 22147072Sbrooks 23147072Sbrooks/* 24147072Sbrooks * Grammar for FTP commands. 25147072Sbrooks * See RFC 959. 26147072Sbrooks */ 27147072Sbrooks 28147072Sbrooks%{ 29147072Sbrooks 30147072Sbrooks#ifndef lint 31147072Sbrooksstatic char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; 32147072Sbrooksstatic char rcsid[] = "$NetBSD: ftp.y,v 1.3 2010/12/24 02:58:21 christos Exp $"; 33147072Sbrooks#endif /* not lint */ 34147072Sbrooks 35147072Sbrooks#include <sys/param.h> 36147072Sbrooks#include <sys/socket.h> 37147072Sbrooks 38147072Sbrooks#include <netinet/in.h> 39147072Sbrooks 40147072Sbrooks#include <arpa/ftp.h> 41147072Sbrooks 42147072Sbrooks#include <stdlib.h> 43149399Sbrooks#include <unistd.h> 44149399Sbrooks#include <stdio.h> 45149399Sbrooks#include <signal.h> 46147072Sbrooks#include <ctype.h> 47147072Sbrooks#include <pwd.h> 48147072Sbrooks#include <setjmp.h> 49147072Sbrooks#include <syslog.h> 50147072Sbrooks#include <sys/stat.h> 51147072Sbrooks#include <string.h> 52147072Sbrooks#include <time.h> 53147072Sbrooks#include <assert.h> 54147072Sbrooks 55147072Sbrooksextern struct sockaddr_in data_dest; 56147072Sbrooksextern int logged_in; 57147072Sbrooksextern struct passwd *pw; 58147072Sbrooksextern int guest; 59147072Sbrooksextern int logging; 60147072Sbrooksextern int type; 61147072Sbrooksextern int form; 62147072Sbrooksextern int debug; 63147072Sbrooksextern int timeout; 64147072Sbrooksextern int maxtimeout; 65147072Sbrooksextern int pdata; 66147072Sbrooksextern char hostname[], remotehost[]; 67147072Sbrooksextern char proctitle[]; 68147072Sbrooksextern char *globerr; 69147072Sbrooksextern int usedefault; 70147072Sbrooksextern int transflag; 71147072Sbrooksextern char tmpline[]; 72147072Sbrooks 73147072Sbrooksextern char **glob(char *); 74147072Sbrooksextern char *renamefrom(char *); 75147072Sbrooksextern void cwd(const char *); 76147072Sbrooks 77147072Sbrooksextern void dologout(int); 78147072Sbrooksextern void fatal(const char *); 79147072Sbrooksextern void makedir(const char *); 80147072Sbrooksextern void nack(const char *); 81147072Sbrooksextern void pass(const char *); 82147072Sbrooksextern void passive(void); 83147072Sbrooksextern void pwd(void); 84147072Sbrooksextern void removedir(char *); 85147072Sbrooksextern void renamecmd(char *, char *); 86226345Sdesextern void retrieve(const char *, const char *); 87226345Sdesextern void send_file_list(const char *); 88147072Sbrooksextern void statcmd(void); 89147072Sbrooksextern void statfilecmd(const char *); 90147072Sbrooksextern void store(char *, const char *, int); 91147072Sbrooksextern void user(const char *); 92147072Sbrooks 93147072Sbrooksextern void perror_reply(int, const char *, ...); 94147072Sbrooksextern void reply(int, const char *, ...); 95147072Sbrooksextern void lreply(int, const char *, ...); 96147072Sbrooks 97147072Sbrooksstatic int cmd_type; 98147072Sbrooksstatic int cmd_form; 99147072Sbrooksstatic int cmd_bytesz; 100147072Sbrookschar cbuf[512]; 101147072Sbrookschar *fromname; 102147072Sbrooks 103147072Sbrooks 104147072Sbrooks 105147072Sbrooksstatic char * copy(const char *); 106147072Sbrooks 107147072Sbrooksstatic void 108147072Sbrooksyyerror(const char *msg) 109147072Sbrooks{ 110147072Sbrooks perror(msg); 111147072Sbrooks} 112147072Sbrooks%} 113147072Sbrooks 114147072Sbrooks%token 115147072Sbrooks A B C E F I 116147072Sbrooks L N P R S T 117147072Sbrooks 118147072Sbrooks SP CRLF COMMA STRING NUMBER 119147072Sbrooks 120147072Sbrooks USER PASS ACCT REIN QUIT PORT 121147072Sbrooks PASV TYPE STRU MODE RETR STOR 122147072Sbrooks APPE MLFL MAIL MSND MSOM MSAM 123147072Sbrooks MRSQ MRCP ALLO REST RNFR RNTO 124147072Sbrooks ABOR DELE CWD LIST NLST SITE 125147072Sbrooks STAT HELP NOOP MKD RMD PWD 126147072Sbrooks CDUP STOU SMNT SYST SIZE MDTM 127147072Sbrooks 128147072Sbrooks UMASK IDLE CHMOD 129147072Sbrooks 130147072Sbrooks LEXERR 131147072Sbrooks 132147072Sbrooks%start cmd_list 133147072Sbrooks 134147072Sbrooks%% 135147072Sbrooks 136147072Sbrookscmd_list: /* empty */ 137147072Sbrooks | cmd_list cmd 138147072Sbrooks = { 139147072Sbrooks fromname = (char *) 0; 140147072Sbrooks } 141147072Sbrooks | cmd_list rcmd 142147072Sbrooks ; 143147072Sbrooks 144147072Sbrookscmd: USER SP username CRLF 145147072Sbrooks = { 146147072Sbrooks user((char *) $3); 147147072Sbrooks free((char *) $3); 148147072Sbrooks } 149147072Sbrooks | PASS SP password CRLF 150147072Sbrooks = { 151147072Sbrooks pass((char *) $3); 152147072Sbrooks free((char *) $3); 153147072Sbrooks } 154147072Sbrooks | PORT SP host_port CRLF 155147072Sbrooks = { 156147072Sbrooks usedefault = 0; 157147072Sbrooks if (pdata >= 0) { 158147072Sbrooks (void) close(pdata); 159147072Sbrooks pdata = -1; 160147072Sbrooks } 161147072Sbrooks reply(200, "PORT command successful."); 162147072Sbrooks } 163147072Sbrooks | PASV CRLF 164147072Sbrooks = { 165147072Sbrooks passive(); 166147072Sbrooks } 167147072Sbrooks | TYPE SP type_code CRLF 168147072Sbrooks = { 169147072Sbrooks switch (cmd_type) { 170147072Sbrooks 171147072Sbrooks case TYPE_A: 172147072Sbrooks if (cmd_form == FORM_N) { 173147072Sbrooks reply(200, "Type set to A."); 174147072Sbrooks type = cmd_type; 175147072Sbrooks form = cmd_form; 176147072Sbrooks } else 177147072Sbrooks reply(504, "Form must be N."); 178147072Sbrooks break; 179147072Sbrooks 180147072Sbrooks case TYPE_E: 181147072Sbrooks reply(504, "Type E not implemented."); 182147072Sbrooks break; 183147072Sbrooks 184147072Sbrooks case TYPE_I: 185147072Sbrooks reply(200, "Type set to I."); 186147072Sbrooks type = cmd_type; 187147072Sbrooks break; 188147072Sbrooks 189147072Sbrooks case TYPE_L: 190147072Sbrooks#if NBBY == 8 191147072Sbrooks if (cmd_bytesz == 8) { 192147072Sbrooks reply(200, 193147072Sbrooks "Type set to L (byte size 8)."); 194147072Sbrooks type = cmd_type; 195147072Sbrooks } else 196147072Sbrooks reply(504, "Byte size must be 8."); 197147072Sbrooks#else /* NBBY == 8 */ 198147072Sbrooks UNIMPLEMENTED for NBBY != 8 199147072Sbrooks#endif /* NBBY == 8 */ 200147072Sbrooks } 201147072Sbrooks } 202147072Sbrooks | STRU SP struct_code CRLF 203147072Sbrooks = { 204147072Sbrooks switch ($3) { 205147072Sbrooks 206147072Sbrooks case STRU_F: 207147072Sbrooks reply(200, "STRU F ok."); 208147072Sbrooks break; 209147072Sbrooks 210147072Sbrooks default: 211147072Sbrooks reply(504, "Unimplemented STRU type."); 212147072Sbrooks } 213147072Sbrooks } 214147072Sbrooks | MODE SP mode_code CRLF 215147072Sbrooks = { 216147072Sbrooks switch ($3) { 217147072Sbrooks 218147072Sbrooks case MODE_S: 219147072Sbrooks reply(200, "MODE S ok."); 220147072Sbrooks break; 221147072Sbrooks 222147072Sbrooks default: 223147072Sbrooks reply(502, "Unimplemented MODE type."); 224147072Sbrooks } 225147072Sbrooks } 226147072Sbrooks | ALLO SP NUMBER CRLF 227147072Sbrooks = { 228147072Sbrooks reply(202, "ALLO command ignored."); 229147072Sbrooks } 230147072Sbrooks | ALLO SP NUMBER SP R SP NUMBER CRLF 231147072Sbrooks = { 232147072Sbrooks reply(202, "ALLO command ignored."); 233147072Sbrooks } 234147072Sbrooks | RETR check_login SP pathname CRLF 235147072Sbrooks = { 236147072Sbrooks if ($2 && $4 != 0) 237147072Sbrooks retrieve((char *) 0, (char *) $4); 238147072Sbrooks if ($4 != 0) 239147072Sbrooks free((char *) $4); 240 } 241 | STOR check_login SP pathname CRLF 242 = { 243 if ($2 && $4 != 0) 244 store((char *) $4, "w", 0); 245 if ($4 != 0) 246 free((char *) $4); 247 } 248 | APPE check_login SP pathname CRLF 249 = { 250 if ($2 && $4 != 0) 251 store((char *) $4, "a", 0); 252 if ($4 != 0) 253 free((char *) $4); 254 } 255 | NLST check_login CRLF 256 = { 257 if ($2) 258 send_file_list("."); 259 } 260 | NLST check_login SP STRING CRLF 261 = { 262 if ($2 && $4 != 0) 263 send_file_list((char *) $4); 264 if ($4 != 0) 265 free((char *) $4); 266 } 267 | LIST check_login CRLF 268 = { 269 if ($2) 270 retrieve("/bin/ls -lgA", ""); 271 } 272 | LIST check_login SP pathname CRLF 273 = { 274 if ($2 && $4 != 0) 275 retrieve("/bin/ls -lgA %s", (char *) $4); 276 if ($4 != 0) 277 free((char *) $4); 278 } 279 | STAT check_login SP pathname CRLF 280 = { 281 if ($2 && $4 != 0) 282 statfilecmd((char *) $4); 283 if ($4 != 0) 284 free((char *) $4); 285 } 286 | STAT CRLF 287 = { 288 statcmd(); 289 } 290 | DELE check_login SP pathname CRLF 291 = { 292 if ($2 && $4 != 0) 293 remove((char *) $4); 294 if ($4 != 0) 295 free((char *) $4); 296 } 297 | RNTO SP pathname CRLF 298 = { 299 if (fromname) { 300 renamecmd(fromname, (char *) $3); 301 free(fromname); 302 fromname = (char *) 0; 303 } else { 304 reply(503, "Bad sequence of commands."); 305 } 306 free((char *) $3); 307 } 308 | ABOR CRLF 309 = { 310 reply(225, "ABOR command successful."); 311 } 312 | CWD check_login CRLF 313 = { 314 if ($2) 315 cwd(pw->pw_dir); 316 } 317 | CWD check_login SP pathname CRLF 318 = { 319 if ($2 && $4 != 0) 320 cwd((char *) $4); 321 if ($4 != 0) 322 free((char *) $4); 323 } 324 | HELP CRLF 325 = { 326 help(cmdtab, (char *) 0); 327 } 328 | HELP SP STRING CRLF 329 = { 330 register char *cp = (char *)$3; 331 332 if (strncasecmp(cp, "SITE", 4) == 0) { 333 cp = (char *)$3 + 4; 334 if (*cp == ' ') 335 cp++; 336 if (*cp) 337 help(sitetab, cp); 338 else 339 help(sitetab, (char *) 0); 340 } else 341 help(cmdtab, (char *) $3); 342 } 343 | NOOP CRLF 344 = { 345 reply(200, "NOOP command successful."); 346 } 347 | MKD check_login SP pathname CRLF 348 = { 349 if ($2 && $4 != 0) 350 makedir((char *) $4); 351 if ($4 != 0) 352 free((char *) $4); 353 } 354 | RMD check_login SP pathname CRLF 355 = { 356 if ($2 && $4 != 0) 357 removedir((char *) $4); 358 if ($4 != 0) 359 free((char *) $4); 360 } 361 | PWD check_login CRLF 362 = { 363 if ($2) 364 pwd(); 365 } 366 | CDUP check_login CRLF 367 = { 368 if ($2) 369 cwd(".."); 370 } 371 | SITE SP HELP CRLF 372 = { 373 help(sitetab, (char *) 0); 374 } 375 | SITE SP HELP SP STRING CRLF 376 = { 377 help(sitetab, (char *) $5); 378 } 379 | SITE SP UMASK check_login CRLF 380 = { 381 int oldmask; 382 383 if ($4) { 384 oldmask = umask(0); 385 (void) umask(oldmask); 386 reply(200, "Current UMASK is %03o", oldmask); 387 } 388 } 389 | SITE SP UMASK check_login SP octal_number CRLF 390 = { 391 int oldmask; 392 393 if ($4) { 394 if (($6 == -1) || ($6 > 0777)) { 395 reply(501, "Bad UMASK value"); 396 } else { 397 oldmask = umask($6); 398 reply(200, 399 "UMASK set to %03o (was %03o)", 400 $6, oldmask); 401 } 402 } 403 } 404 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 405 = { 406 if ($4 && ($8 != 0)) { 407 if ($6 > 0777) 408 reply(501, 409 "CHMOD: Mode value must be between 0 and 0777"); 410 else if (chmod((char *) $8, $6) < 0) 411 perror_reply(550, (char *) $8); 412 else 413 reply(200, "CHMOD command successful."); 414 } 415 if ($8 != 0) 416 free((char *) $8); 417 } 418 | SITE SP IDLE CRLF 419 = { 420 reply(200, 421 "Current IDLE time limit is %d seconds; max %d", 422 timeout, maxtimeout); 423 } 424 | SITE SP IDLE SP NUMBER CRLF 425 = { 426 if ($5 < 30 || $5 > maxtimeout) { 427 reply(501, 428 "Maximum IDLE time must be between 30 and %d seconds", 429 maxtimeout); 430 } else { 431 timeout = $5; 432 (void) alarm((unsigned) timeout); 433 reply(200, 434 "Maximum IDLE time set to %d seconds", 435 timeout); 436 } 437 } 438 | STOU check_login SP pathname CRLF 439 = { 440 if ($2 && $4 != 0) 441 store((char *) $4, "w", 1); 442 if ($4 != 0) 443 free((char *) $4); 444 } 445 | SYST CRLF 446 = { 447#ifdef unix 448#ifdef BSD 449 reply(215, "UNIX Type: L%d Version: BSD-%d", 450 NBBY, BSD); 451#else /* BSD */ 452 reply(215, "UNIX Type: L%d", NBBY); 453#endif /* BSD */ 454#else /* unix */ 455 reply(215, "UNKNOWN Type: L%d", NBBY); 456#endif /* unix */ 457 } 458 459 /* 460 * SIZE is not in RFC959, but Postel has blessed it and 461 * it will be in the updated RFC. 462 * 463 * Return size of file in a format suitable for 464 * using with RESTART (we just count bytes). 465 */ 466 | SIZE check_login SP pathname CRLF 467 = { 468 if ($2 && $4 != 0) 469 sizecmd((char *) $4); 470 if ($4 != 0) 471 free((char *) $4); 472 } 473 474 /* 475 * MDTM is not in RFC959, but Postel has blessed it and 476 * it will be in the updated RFC. 477 * 478 * Return modification time of file as an ISO 3307 479 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 480 * where xxx is the fractional second (of any precision, 481 * not necessarily 3 digits) 482 */ 483 | MDTM check_login SP pathname CRLF 484 = { 485 if ($2 && $4 != 0) { 486 struct stat stbuf; 487 if (stat((char *) $4, &stbuf) < 0) 488 perror_reply(550, "%s", (char *) $4); 489 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 490 reply(550, "%s: not a plain file.", 491 (char *) $4); 492 } else { 493 register struct tm *t; 494 t = gmtime(&stbuf.st_mtime); 495 reply(213, 496 "%04d%02d%02d%02d%02d%02d", 497 1900 + t->tm_year, 498 t->tm_mon+1, t->tm_mday, 499 t->tm_hour, t->tm_min, t->tm_sec); 500 } 501 } 502 if ($4 != 0) 503 free((char *) $4); 504 } 505 | QUIT CRLF 506 = { 507 reply(221, "Goodbye."); 508 dologout(0); 509 } 510 | error CRLF 511 = { 512 yyerrok; 513 } 514 ; 515rcmd: RNFR check_login SP pathname CRLF 516 = { 517 if ($2 && $4) { 518 fromname = renamefrom((char *) $4); 519 if (fromname == (char *) 0 && $4) { 520 free((char *) $4); 521 } 522 } 523 } 524 ; 525 526username: STRING 527 ; 528 529password: /* empty */ 530 = { 531 *(const char **)(&($$)) = ""; 532 } 533 | STRING 534 ; 535 536byte_size: NUMBER 537 ; 538 539host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 540 NUMBER COMMA NUMBER 541 = { 542 register char *a, *p; 543 544 a = (char *)&data_dest.sin_addr; 545 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 546 p = (char *)&data_dest.sin_port; 547 p[0] = $9; p[1] = $11; 548 data_dest.sin_family = AF_INET; 549 } 550 ; 551 552form_code: N 553 = { 554 $$ = FORM_N; 555 } 556 | T 557 = { 558 $$ = FORM_T; 559 } 560 | C 561 = { 562 $$ = FORM_C; 563 } 564 ; 565 566type_code: A 567 = { 568 cmd_type = TYPE_A; 569 cmd_form = FORM_N; 570 } 571 | A SP form_code 572 = { 573 cmd_type = TYPE_A; 574 cmd_form = $3; 575 } 576 | E 577 = { 578 cmd_type = TYPE_E; 579 cmd_form = FORM_N; 580 } 581 | E SP form_code 582 = { 583 cmd_type = TYPE_E; 584 cmd_form = $3; 585 } 586 | I 587 = { 588 cmd_type = TYPE_I; 589 } 590 | L 591 = { 592 cmd_type = TYPE_L; 593 cmd_bytesz = NBBY; 594 } 595 | L SP byte_size 596 = { 597 cmd_type = TYPE_L; 598 cmd_bytesz = $3; 599 } 600 /* this is for a bug in the BBN ftp */ 601 | L byte_size 602 = { 603 cmd_type = TYPE_L; 604 cmd_bytesz = $2; 605 } 606 ; 607 608struct_code: F 609 = { 610 $$ = STRU_F; 611 } 612 | R 613 = { 614 $$ = STRU_R; 615 } 616 | P 617 = { 618 $$ = STRU_P; 619 } 620 ; 621 622mode_code: S 623 = { 624 $$ = MODE_S; 625 } 626 | B 627 = { 628 $$ = MODE_B; 629 } 630 | C 631 = { 632 $$ = MODE_C; 633 } 634 ; 635 636pathname: pathstring 637 = { 638 /* 639 * Problem: this production is used for all pathname 640 * processing, but only gives a 550 error reply. 641 * This is a valid reply in some cases but not in others. 642 */ 643 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 644 *(char **)&($$) = *glob((char *) $1); 645 if (globerr != 0) { 646 reply(550, globerr); 647 $$ = 0; 648 } 649 free((char *) $1); 650 } else 651 $$ = $1; 652 } 653 ; 654 655pathstring: STRING 656 ; 657 658octal_number: NUMBER 659 = { 660 register int ret, dec, multby, digit; 661 662 /* 663 * Convert a number that was read as decimal number 664 * to what it would be if it had been read as octal. 665 */ 666 dec = $1; 667 multby = 1; 668 ret = 0; 669 while (dec) { 670 digit = dec%10; 671 if (digit > 7) { 672 ret = -1; 673 break; 674 } 675 ret += digit * multby; 676 multby *= 8; 677 dec /= 10; 678 } 679 $$ = ret; 680 } 681 ; 682 683check_login: /* empty */ 684 = { 685 if (logged_in) 686 $$ = 1; 687 else { 688 reply(530, "Please login with USER and PASS."); 689 $$ = 0; 690 } 691 } 692 ; 693 694%% 695 696#ifdef YYBYACC 697extern int YYLEX_DECL(); 698static void YYERROR_DECL(); 699#endif 700 701extern jmp_buf errcatch; 702 703static void upper(char *); 704 705#define CMD 0 /* beginning of command */ 706#define ARGS 1 /* expect miscellaneous arguments */ 707#define STR1 2 /* expect SP followed by STRING */ 708#define STR2 3 /* expect STRING */ 709#define OSTR 4 /* optional SP then STRING */ 710#define ZSTR1 5 /* SP then optional STRING */ 711#define ZSTR2 6 /* optional STRING after SP */ 712#define SITECMD 7 /* SITE command */ 713#define NSTR 8 /* Number followed by a string */ 714 715struct tab { 716 const char *name; 717 short token; 718 short state; 719 short implemented; /* 1 if command is implemented */ 720 const char *help; 721}; 722 723struct tab cmdtab[] = { /* In order defined in RFC 765 */ 724 { "USER", USER, STR1, 1, "<sp> username" }, 725 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 726 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 727 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 728 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 729 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 730 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 731 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 732 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 733 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 734 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 735 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 736 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 737 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 738 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 739 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 740 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 741 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 742 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 743 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 744 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 745 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 746 { "REST", REST, ARGS, 0, "(restart command)" }, 747 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 748 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 749 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 750 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 751 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 752 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 753 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 754 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 755 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 756 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 757 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 758 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 759 { "NOOP", NOOP, ARGS, 1, "" }, 760 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 761 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 762 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 763 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 764 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 765 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 766 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 767 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 768 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 769 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 770 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 771 { 0, 0, 0, 0, 0 } 772}; 773 774struct tab sitetab[] = { 775 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 776 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 777 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 778 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 779 { 0, 0, 0, 0, 0 } 780}; 781 782static struct tab * 783lookup(struct tab *p, char *cmd) 784{ 785 786 for (; p->name != 0; p++) 787 if (strcmp(cmd, p->name) == 0) 788 return (p); 789 return (0); 790} 791 792#include <arpa/telnet.h> 793 794/* 795 * get_line - a hacked up version of fgets to ignore TELNET escape codes. 796 */ 797static char * 798get_line(char *s, int n, FILE *iop) 799{ 800 register int c; 801 register char *cs; 802 803 cs = s; 804/* tmpline may contain saved command from urgent mode interruption */ 805 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 806 *cs++ = tmpline[c]; 807 if (tmpline[c] == '\n') { 808 *cs = '\0'; 809 if (debug) 810 syslog(LOG_DEBUG, "command: %s", s); 811 tmpline[0] = '\0'; 812 return(s); 813 } 814 if (c == 0) 815 tmpline[0] = '\0'; 816 } 817 while ((c = getc(iop)) != EOF) { 818 c &= 0377; 819 if (c == IAC) { 820 if ((c = getc(iop)) != EOF) { 821 c &= 0377; 822 switch (c) { 823 case WILL: 824 case WONT: 825 c = getc(iop); 826 printf("%c%c%c", IAC, DONT, 0377&c); 827 (void) fflush(stdout); 828 continue; 829 case DO: 830 case DONT: 831 c = getc(iop); 832 printf("%c%c%c", IAC, WONT, 0377&c); 833 (void) fflush(stdout); 834 continue; 835 case IAC: 836 break; 837 default: 838 continue; /* ignore command */ 839 } 840 } 841 } 842 *cs++ = c; 843 if (--n <= 0 || c == '\n') 844 break; 845 } 846 if (c == EOF && cs == s) 847 return (0); 848 *cs = '\0'; 849 if (debug) 850 syslog(LOG_DEBUG, "command: %s", s); 851 return (s); 852} 853 854static void 855toolong(int sig) 856{ 857 time_t now; 858 859 (void) sig; 860 reply(421, 861 "Timeout (%d seconds): closing control connection.", timeout); 862 (void) time(&now); 863 if (logging) { 864 syslog(LOG_INFO, 865 "User %s timed out after %d seconds at %s", 866 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 867 } 868 dologout(1); 869} 870 871int 872yylex(void) 873{ 874 static int cpos, state; 875 register char *cp, *cp2; 876 register struct tab *p; 877 int n; 878 char c; 879 880 for (;;) { 881 switch (state) { 882 883 case CMD: 884 (void) signal(SIGALRM, toolong); 885 (void) alarm((unsigned) timeout); 886 if (get_line(cbuf, sizeof(cbuf)-1, stdin) == 0) { 887 reply(221, "You could at least say goodbye."); 888 dologout(0); 889 } 890 (void) alarm(0); 891#ifdef SETPROCTITLE 892 if (strncasecmp(cbuf, "PASS", 4) != 0) 893 setproctitle("%s: %s", proctitle, cbuf); 894#endif /* SETPROCTITLE */ 895 if ((cp = strchr(cbuf, '\r'))) { 896 *cp++ = '\n'; 897 *cp = '\0'; 898 } 899 if ((cp = strpbrk(cbuf, " \n"))) 900 cpos = cp - cbuf; 901 if (cpos == 0) 902 cpos = 4; 903 c = cbuf[cpos]; 904 cbuf[cpos] = '\0'; 905 upper(cbuf); 906 p = lookup(cmdtab, cbuf); 907 cbuf[cpos] = c; 908 if (p != 0) { 909 if (p->implemented == 0) { 910 nack(p->name); 911 longjmp(errcatch,0); 912 /* NOTREACHED */ 913 } 914 state = p->state; 915 *(const char **)(&yylval) = p->name; 916 return (p->token); 917 } 918 break; 919 920 case SITECMD: 921 if (cbuf[cpos] == ' ') { 922 cpos++; 923 return (SP); 924 } 925 cp = &cbuf[cpos]; 926 if ((cp2 = strpbrk(cp, " \n"))) 927 cpos = cp2 - cbuf; 928 c = cbuf[cpos]; 929 cbuf[cpos] = '\0'; 930 upper(cp); 931 p = lookup(sitetab, cp); 932 cbuf[cpos] = c; 933 if (p != 0) { 934 if (p->implemented == 0) { 935 state = CMD; 936 nack(p->name); 937 longjmp(errcatch,0); 938 /* NOTREACHED */ 939 } 940 state = p->state; 941 *(const char **)(&yylval) = p->name; 942 return (p->token); 943 } 944 state = CMD; 945 break; 946 947 case OSTR: 948 if (cbuf[cpos] == '\n') { 949 state = CMD; 950 return (CRLF); 951 } 952 /* FALLTHROUGH */ 953 954 case STR1: 955 case ZSTR1: 956 dostr1: 957 if (cbuf[cpos] == ' ') { 958 cpos++; 959 if (state == OSTR) 960 state = STR2; 961 else 962 ++state; 963 return (SP); 964 } 965 break; 966 967 case ZSTR2: 968 if (cbuf[cpos] == '\n') { 969 state = CMD; 970 return (CRLF); 971 } 972 /* FALLTHROUGH */ 973 974 case STR2: 975 cp = &cbuf[cpos]; 976 n = strlen(cp); 977 cpos += n - 1; 978 /* 979 * Make sure the string is nonempty and \n terminated. 980 */ 981 if (n > 1 && cbuf[cpos] == '\n') { 982 cbuf[cpos] = '\0'; 983 *(char **)&yylval = copy(cp); 984 cbuf[cpos] = '\n'; 985 state = ARGS; 986 return (STRING); 987 } 988 break; 989 990 case NSTR: 991 if (cbuf[cpos] == ' ') { 992 cpos++; 993 return (SP); 994 } 995 if (isdigit(cbuf[cpos])) { 996 cp = &cbuf[cpos]; 997 while (isdigit(cbuf[++cpos])) 998 ; 999 c = cbuf[cpos]; 1000 cbuf[cpos] = '\0'; 1001 yylval = atoi(cp); 1002 cbuf[cpos] = c; 1003 state = STR1; 1004 return (NUMBER); 1005 } 1006 state = STR1; 1007 goto dostr1; 1008 1009 case ARGS: 1010 if (isdigit(cbuf[cpos])) { 1011 cp = &cbuf[cpos]; 1012 while (isdigit(cbuf[++cpos])) 1013 ; 1014 c = cbuf[cpos]; 1015 cbuf[cpos] = '\0'; 1016 yylval = atoi(cp); 1017 cbuf[cpos] = c; 1018 return (NUMBER); 1019 } 1020 switch (cbuf[cpos++]) { 1021 1022 case '\n': 1023 state = CMD; 1024 return (CRLF); 1025 1026 case ' ': 1027 return (SP); 1028 1029 case ',': 1030 return (COMMA); 1031 1032 case 'A': 1033 case 'a': 1034 return (A); 1035 1036 case 'B': 1037 case 'b': 1038 return (B); 1039 1040 case 'C': 1041 case 'c': 1042 return (C); 1043 1044 case 'E': 1045 case 'e': 1046 return (E); 1047 1048 case 'F': 1049 case 'f': 1050 return (F); 1051 1052 case 'I': 1053 case 'i': 1054 return (I); 1055 1056 case 'L': 1057 case 'l': 1058 return (L); 1059 1060 case 'N': 1061 case 'n': 1062 return (N); 1063 1064 case 'P': 1065 case 'p': 1066 return (P); 1067 1068 case 'R': 1069 case 'r': 1070 return (R); 1071 1072 case 'S': 1073 case 's': 1074 return (S); 1075 1076 case 'T': 1077 case 't': 1078 return (T); 1079 1080 } 1081 break; 1082 1083 default: 1084 fatal("Unknown state in scanner."); 1085 } 1086 yyerror((char *) 0); 1087 state = CMD; 1088 longjmp(errcatch,0); 1089 } 1090} 1091 1092static void 1093upper(char *s) 1094{ 1095 while (*s != '\0') { 1096 if (islower(*s)) 1097 *s = toupper(*s); 1098 s++; 1099 } 1100} 1101 1102static char * 1103copy(const char *s) 1104{ 1105 char *p; 1106 1107 p = (char * )malloc(strlen(s) + 1); 1108 if (p == 0) 1109 fatal("Ran out of memory."); 1110 else 1111 (void) strcpy(p, s); 1112 return (p); 1113} 1114 1115static void 1116help(struct tab *ctab, char *s) 1117{ 1118 register struct tab *c; 1119 register int width, NCMDS; 1120 const char *help_type; 1121 1122 if (ctab == sitetab) 1123 help_type = "SITE "; 1124 else 1125 help_type = ""; 1126 width = 0, NCMDS = 0; 1127 for (c = ctab; c->name != 0; c++) { 1128 int len = strlen(c->name); 1129 1130 if (len > width) 1131 width = len; 1132 NCMDS++; 1133 } 1134 width = (width + 8) &~ 7; 1135 if (s == 0) { 1136 register int i, j, w; 1137 int columns, lines; 1138 1139 lreply(214, "The following %scommands are recognized %s.", 1140 help_type, "(* =>'s unimplemented)"); 1141 columns = 76 / width; 1142 if (columns == 0) 1143 columns = 1; 1144 lines = (NCMDS + columns - 1) / columns; 1145 for (i = 0; i < lines; i++) { 1146 printf(" "); 1147 for (j = 0; j < columns; j++) { 1148 c = ctab + j * lines + i; 1149 assert(c->name != 0); 1150 printf("%s%c", c->name, 1151 c->implemented ? ' ' : '*'); 1152 if (c + lines >= &ctab[NCMDS]) 1153 break; 1154 w = strlen(c->name) + 1; 1155 while (w < width) { 1156 putchar(' '); 1157 w++; 1158 } 1159 } 1160 printf("\r\n"); 1161 } 1162 (void) fflush(stdout); 1163 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1164 return; 1165 } 1166 upper(s); 1167 c = lookup(ctab, s); 1168 if (c == (struct tab *)0) { 1169 reply(502, "Unknown command %s.", s); 1170 return; 1171 } 1172 if (c->implemented) 1173 reply(214, "Syntax: %s%s %s", help_type, c->name, c->help); 1174 else 1175 reply(214, "%s%-*s\t%s; unimplemented.", help_type, width, 1176 c->name, c->help); 1177} 1178 1179static void 1180sizecmd(char *filename) 1181{ 1182 switch (type) { 1183 case TYPE_L: 1184 case TYPE_I: { 1185 struct stat stbuf; 1186 if (stat(filename, &stbuf) < 0 || 1187 (stbuf.st_mode&S_IFMT) != S_IFREG) 1188 reply(550, "%s: not a plain file.", filename); 1189 else 1190#ifdef HAVE_LONG_LONG 1191 reply(213, "%llu", (long long) stbuf.st_size); 1192#else 1193 reply(213, "%lu", stbuf.st_size); 1194#endif 1195 break;} 1196 case TYPE_A: { 1197 FILE *fin; 1198 register int c, count; 1199 struct stat stbuf; 1200 fin = fopen(filename, "r"); 1201 if (fin == 0) { 1202 perror_reply(550, filename); 1203 return; 1204 } 1205 if (fstat(fileno(fin), &stbuf) < 0 || 1206 (stbuf.st_mode&S_IFMT) != S_IFREG) { 1207 reply(550, "%s: not a plain file.", filename); 1208 (void) fclose(fin); 1209 return; 1210 } 1211 1212 count = 0; 1213 while((c=getc(fin)) != EOF) { 1214 if (c == '\n') /* will get expanded to \r\n */ 1215 count++; 1216 count++; 1217 } 1218 (void) fclose(fin); 1219 1220 reply(213, "%ld", count); 1221 break;} 1222 default: 1223 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1224 } 1225} 1226