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: }

Defined functions

_copy defined in line 1073; used 2 times
_getline defined in line 768; used 3 times
_help defined in line 1087; used 9 times
_lookup defined in line 751; used 3 times
_sizecmd defined in line 1151; used 1 times
_toolong defined in line 827; used 1 times
_upper defined in line 1063; used 4 times
_yylex defined in line 845; never used

Defined variables

_cmdtab defined in line 692; used 3 times
_sitetab defined in line 743; used 6 times

Defined struct's

tab defined in line 684; used 16 times

Defined macros

ARGS defined in line 675; used 19 times
CMD defined in line 674; used 6 times
NSTR defined in line 682; used 1 times
OSTR defined in line 678; used 16 times
SITECMD defined in line 681; used 1 times
STR1 defined in line 676; used 17 times
STR2 defined in line 677; used 1 times
ZSTR1 defined in line 679; used 1 times
ZSTR2 defined in line 680; never used
Last modified: 1990-02-21
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 6846
Valid CSS Valid XHTML 1.0 Strict