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