|
|
1.1 ! root 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 ! 28: static 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: ! 47: extern struct sockaddr_in data_dest; ! 48: extern int logged_in; ! 49: extern struct passwd *pw; ! 50: extern int guest; ! 51: extern int logging; ! 52: extern int type; ! 53: extern int form; ! 54: extern int debug; ! 55: extern int timeout; ! 56: extern int maxtimeout; ! 57: extern int pdata; ! 58: extern char hostname[], remotehost[]; ! 59: extern char proctitle[]; ! 60: extern char *globerr; ! 61: extern int usedefault; ! 62: extern int transflag; ! 63: extern char tmpline[]; ! 64: char **glob(); ! 65: ! 66: static int cmd_type; ! 67: static int cmd_form; ! 68: static int cmd_bytesz; ! 69: char cbuf[512]; ! 70: char *fromname; ! 71: ! 72: char *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: ! 97: cmd_list: /* empty */ ! 98: | cmd_list cmd ! 99: = { ! 100: fromname = (char *) 0; ! 101: } ! 102: | cmd_list rcmd ! 103: ; ! 104: ! 105: cmd: 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: ; ! 476: rcmd: 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: ! 489: username: STRING ! 490: ; ! 491: ! 492: password: /* empty */ ! 493: = { ! 494: *(char **)&($$) = ""; ! 495: } ! 496: | STRING ! 497: ; ! 498: ! 499: byte_size: NUMBER ! 500: ; ! 501: ! 502: host_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: ! 515: form_code: N ! 516: = { ! 517: $$ = FORM_N; ! 518: } ! 519: | T ! 520: = { ! 521: $$ = FORM_T; ! 522: } ! 523: | C ! 524: = { ! 525: $$ = FORM_C; ! 526: } ! 527: ; ! 528: ! 529: type_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: ! 571: struct_code: F ! 572: = { ! 573: $$ = STRU_F; ! 574: } ! 575: | R ! 576: = { ! 577: $$ = STRU_R; ! 578: } ! 579: | P ! 580: = { ! 581: $$ = STRU_P; ! 582: } ! 583: ; ! 584: ! 585: mode_code: S ! 586: = { ! 587: $$ = MODE_S; ! 588: } ! 589: | B ! 590: = { ! 591: $$ = MODE_B; ! 592: } ! 593: | C ! 594: = { ! 595: $$ = MODE_C; ! 596: } ! 597: ; ! 598: ! 599: pathname: 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: ! 618: pathstring: STRING ! 619: ; ! 620: ! 621: octal_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: ! 646: check_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: ! 659: extern 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: ! 671: struct tab { ! 672: char *name; ! 673: short token; ! 674: short state; ! 675: short implemented; /* 1 if command is implemented */ ! 676: char *help; ! 677: }; ! 678: ! 679: struct 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: ! 730: struct 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: ! 738: struct tab * ! 739: lookup(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: */ ! 755: char * ! 756: getline(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: ! 814: static int ! 815: toolong() ! 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: ! 832: yylex() ! 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: ! 1050: upper(s) ! 1051: register char *s; ! 1052: { ! 1053: while (*s != '\0') { ! 1054: if (islower(*s)) ! 1055: *s = toupper(*s); ! 1056: s++; ! 1057: } ! 1058: } ! 1059: ! 1060: char * ! 1061: copy(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: ! 1074: help(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: ! 1138: sizecmd(filename) ! 1139: char *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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.