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