1 /* 2 * Copyright (c) 1985, 1989 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 18 /* 19 * FTP User Program -- Command Interface. 20 */ 21 22 #include "precomp.h" 23 24 #include <fcntl.h> 25 26 #ifndef lint 27 char copyright[] = 28 "@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\ 29 All rights reserved.\n"; 30 #endif /* not lint */ 31 32 #ifndef lint 33 static char sccsid[] = "@(#)main.c based on 5.13 (Berkeley) 3/14/89"; 34 #endif /* not lint */ 35 36 #if defined(sun) && !defined(FD_SET) 37 typedef int uid_t; 38 #endif 39 40 uid_t getuid(void); 41 void intr(void); 42 void lostpeer(void); 43 char *getlogin(void); 44 45 short portnum; 46 47 char home[128]; 48 char *globerr; 49 int autologin; 50 51 52 53 /* Lot's of options... */ 54 /* 55 * Options and other state info. 56 */ 57 int trace; /* trace packets exchanged */ 58 int hash; /* print # for each buffer transferred */ 59 //int sendport; /* use PORT cmd for each data connection */ 60 int verbose; /* print messages coming back from server */ 61 int connected; /* connected to server */ 62 int fromatty; /* input is from a terminal */ 63 int interactive; /* interactively prompt on m* cmds */ 64 int debug; /* debugging level */ 65 int bell; /* ring bell on cmd completion */ 66 int doglob; /* glob local file names */ 67 int proxy; /* proxy server connection active */ 68 int passivemode; 69 int proxflag; /* proxy connection exists */ 70 int sunique; /* store files on server with unique name */ 71 int runique; /* store local files with unique name */ 72 int mcase; /* map upper to lower case for mget names */ 73 int ntflag; /* use ntin ntout tables for name translation */ 74 int mapflag; /* use mapin mapout templates on file names */ 75 int code; /* return/reply code for ftp command */ 76 int crflag; /* if 1, strip car. rets. on ascii gets */ 77 char pasv[64]; /* passive port for proxy data connection */ 78 char *altarg; /* argv[1] with no shell-like preprocessing */ 79 char ntin[17]; /* input translation table */ 80 char ntout[17]; /* output translation table */ 81 // #include <sys/param.h> 82 char mapin[MAXPATHLEN]; /* input map template */ 83 char mapout[MAXPATHLEN]; /* output map template */ 84 char typename[32]; /* name of file transfer type */ 85 int type; /* file transfer type */ 86 char structname[32]; /* name of file transfer structure */ 87 int stru; /* file transfer structure */ 88 char formname[32]; /* name of file transfer format */ 89 int form; /* file transfer format */ 90 char modename[32]; /* name of file transfer mode */ 91 int mode; /* file transfer mode */ 92 char bytename[32]; /* local byte size in ascii */ 93 int bytesize; /* local byte size in binary */ 94 95 jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ 96 97 char line[200]; /* input line buffer */ 98 char *stringbase; /* current scan point in line buffer */ 99 char argbuf[200]; /* argument storage buffer */ 100 char *argbase; /* current storage point in arg buffer */ 101 int margc; /* count of arguments on input line */ 102 const char *margv[20]; /* args parsed from input line */ 103 int cpend; /* flag: if != 0, then pending server reply */ 104 int mflag; /* flag: if != 0, then active multi command */ 105 106 int options; /* used during socket creation */ 107 108 int macnum; /* number of defined macros */ 109 struct macel macros[16]; 110 char macbuf[4096]; 111 112 /* 113 * Need to start a listen on the data channel 114 * before we send the command, otherwise the 115 * server's connect may fail. 116 */ 117 int sendport = -1; 118 119 static const char *slurpstring(); 120 121 122 int main(int argc, const char *argv[]) 123 { 124 const char *cp; 125 int top; 126 #if 0 127 char homedir[MAXPATHLEN]; 128 #endif 129 130 int err; 131 WORD wVerReq; 132 133 WSADATA WSAData; 134 struct servent *sp; /* service spec for tcp/ftp */ 135 136 /* Disable output buffering, for the benefit of Emacs. */ 137 //setbuf(stdout, NULL); 138 139 _fmode = O_BINARY; // This causes an error somewhere. 140 141 wVerReq = MAKEWORD(1,1); 142 143 err = WSAStartup(wVerReq, &WSAData); 144 if (err != 0) 145 { 146 fprintf(stderr, "Could not initialize Windows socket interface."); 147 exit(1); 148 } 149 150 sp = getservbyname("ftp", "tcp"); 151 if (sp == 0) { 152 fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 153 exit(1); 154 } 155 156 portnum = sp->s_port; 157 158 159 doglob = 1; 160 interactive = 1; 161 autologin = 1; 162 argc--, argv++; 163 while (argc > 0 && **argv == '-') { 164 for (cp = *argv + 1; *cp; cp++) 165 switch (*cp) { 166 167 case 'd': 168 options |= SO_DEBUG; 169 debug++; 170 break; 171 172 case 'v': 173 verbose++; 174 break; 175 176 case 't': 177 trace++; 178 break; 179 180 case 'i': 181 interactive = 0; 182 break; 183 184 case 'n': 185 autologin = 0; 186 break; 187 188 case 'g': 189 doglob = 0; 190 break; 191 192 default: 193 fprintf(stdout, 194 "ftp: %c: unknown option\n", *cp); 195 exit(1); 196 } 197 argc--, argv++; 198 } 199 // fromatty = isatty(fileno(stdin)); 200 fromatty = 1; // Strengthen this test 201 /* 202 * Set up defaults for FTP. 203 */ 204 (void) strcpy(typename, "ascii"), type = TYPE_A; 205 (void) strcpy(formname, "non-print"), form = FORM_N; 206 (void) strcpy(modename, "stream"), mode = MODE_S; 207 (void) strcpy(structname, "file"), stru = STRU_F; 208 (void) strcpy(bytename, "8"), bytesize = 8; 209 if (fromatty) 210 verbose++; 211 cpend = 0; /* no pending replies */ 212 proxy = 0; /* proxy not active */ 213 passivemode = 1; /* passive mode *is* active */ 214 crflag = 1; /* strip c.r. on ascii gets */ 215 /* 216 * Set up the home directory in case we're globbing. 217 */ 218 #if 0 219 cp = getlogin(); 220 if (cp != NULL) { 221 pw = getpwnam(cp); 222 } 223 if (pw == NULL) 224 pw = getpwuid(getuid()); 225 if (pw != NULL) { 226 home = homedir; 227 (void) strcpy(home, pw->pw_dir); 228 } 229 #endif 230 cp = getenv("SystemDrive"); 231 if (cp != NULL && *cp != 0) 232 { 233 strcpy(home, cp); 234 strcat(home, "/"); 235 } 236 else 237 { 238 strcpy(home, "C:/"); 239 } 240 if (argc > 0) { 241 if (setjmp(toplevel)) 242 exit(0); 243 // (void) signal(SIGINT, intr); 244 // (void) signal(SIGPIPE, lostpeer); 245 setpeer(argc + 1, argv - 1); 246 } 247 top = setjmp(toplevel) == 0; 248 if (top) { 249 // (void) signal(SIGINT, intr); 250 // (void) signal(SIGPIPE, lostpeer); 251 } 252 for (;;) { 253 cmdscanner(top); 254 top = 1; 255 } 256 } 257 258 void intr(void) 259 { 260 longjmp(toplevel, 1); 261 } 262 263 void lostpeer(void) 264 { 265 extern SOCKET cout; 266 extern int data; 267 268 if (connected) { 269 if (cout) { 270 closesocket(cout); 271 cout = 0; 272 } 273 if (data >= 0) { 274 (void) shutdown(data, 1+1); 275 (void) close(data); 276 data = -1; 277 } 278 connected = 0; 279 } 280 pswitch(1); 281 if (connected) { 282 if (cout) { 283 closesocket(cout); 284 cout = 0; 285 } 286 connected = 0; 287 } 288 proxflag = 0; 289 pswitch(0); 290 } 291 292 /*char * 293 tail(char *filename) 294 { 295 register char *s; 296 297 while (*filename) { 298 s = rindex(filename, '/'); 299 if (s == NULL) 300 break; 301 if (s[1]) 302 return (s + 1); 303 *s = '\0'; 304 } 305 return (filename); 306 } 307 */ 308 /* 309 * Command parser. 310 */ 311 void cmdscanner(int top) 312 { 313 register struct cmd *c; 314 315 if (!top) 316 (void) putchar('\n'); 317 for (;;) { 318 (void) fflush(stdout); 319 if (fromatty) { 320 printf("ftp> "); 321 (void) fflush(stdout); 322 } 323 if (gets(line) == 0) { 324 if (feof(stdin) || ferror(stdin)) 325 quit(0, NULL); 326 break; 327 } 328 if (line[0] == 0) 329 break; 330 makeargv(); 331 if (margc == 0) { 332 continue; 333 } 334 c = getcmd(margv[0]); 335 if (c == (struct cmd *)-1) { 336 printf("?Ambiguous command\n"); 337 continue; 338 } 339 if (c == 0) { 340 printf("?Invalid command\n"); 341 continue; 342 } 343 if (c->c_conn && !connected) { 344 printf ("Not connected.\n"); 345 continue; 346 } 347 (*c->c_handler)(margc, margv); 348 if (bell && c->c_bell) 349 (void) putchar('\007'); 350 if (c->c_handler != help) 351 break; 352 } 353 (void) fflush(stdout); 354 // (void) signal(SIGINT, intr); 355 // (void) signal(SIGPIPE, lostpeer); 356 } 357 358 struct cmd * 359 getcmd(const char *name) 360 { 361 extern struct cmd cmdtab[]; 362 const char *p, *q; 363 struct cmd *c, *found; 364 int nmatches, longest; 365 366 longest = 0; 367 nmatches = 0; 368 found = 0; 369 for (c = cmdtab; (p = c->c_name); c++) { 370 for (q = name; *q == *p++; q++) 371 if (*q == 0) /* exact match? */ 372 return (c); 373 if (!*q) { /* the name was a prefix */ 374 if (q - name > longest) { 375 longest = q - name; 376 nmatches = 1; 377 found = c; 378 } else if (q - name == longest) 379 nmatches++; 380 } 381 } 382 if (nmatches > 1) 383 return ((struct cmd *)-1); 384 return (found); 385 } 386 387 /* 388 * Slice a string up into argc/argv. 389 */ 390 391 int slrflag; 392 393 void makeargv(void) 394 { 395 const char **argp; 396 397 margc = 0; 398 argp = margv; 399 stringbase = line; /* scan from first of buffer */ 400 argbase = argbuf; /* store from first of buffer */ 401 slrflag = 0; 402 while ((*argp++ = slurpstring())) 403 margc++; 404 } 405 406 /* 407 * Parse string into argbuf; 408 * implemented with FSM to 409 * handle quoting and strings 410 */ 411 static const char * 412 slurpstring(void) 413 { 414 int got_one = 0; 415 register char *sb = stringbase; 416 register char *ap = argbase; 417 char *tmp = argbase; /* will return this if token found */ 418 419 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 420 switch (slrflag) { /* and $ as token for macro invoke */ 421 case 0: 422 slrflag++; 423 stringbase++; 424 return ((*sb == '!') ? "!" : "$"); 425 /* NOTREACHED */ 426 case 1: 427 slrflag++; 428 altarg = stringbase; 429 break; 430 default: 431 break; 432 } 433 } 434 435 S0: 436 switch (*sb) { 437 438 case '\0': 439 goto OUT1; 440 441 case ' ': 442 case '\t': 443 sb++; goto S0; 444 445 default: 446 switch (slrflag) { 447 case 0: 448 slrflag++; 449 break; 450 case 1: 451 slrflag++; 452 altarg = sb; 453 break; 454 default: 455 break; 456 } 457 goto S1; 458 } 459 460 S1: 461 switch (*sb) { 462 463 case ' ': 464 case '\t': 465 case '\0': 466 goto OUT1; /* end of token */ 467 468 case '\\': 469 sb++; goto S2; /* slurp next character */ 470 471 case '"': 472 sb++; goto S3; /* slurp quoted string */ 473 474 default: 475 *ap++ = *sb++; /* add character to token */ 476 got_one = 1; 477 goto S1; 478 } 479 480 S2: 481 switch (*sb) { 482 483 case '\0': 484 goto OUT1; 485 486 default: 487 *ap++ = *sb++; 488 got_one = 1; 489 goto S1; 490 } 491 492 S3: 493 switch (*sb) { 494 495 case '\0': 496 goto OUT1; 497 498 case '"': 499 sb++; goto S1; 500 501 default: 502 *ap++ = *sb++; 503 got_one = 1; 504 goto S3; 505 } 506 507 OUT1: 508 if (got_one) 509 *ap++ = '\0'; 510 argbase = ap; /* update storage pointer */ 511 stringbase = sb; /* update scan pointer */ 512 if (got_one) { 513 return(tmp); 514 } 515 switch (slrflag) { 516 case 0: 517 slrflag++; 518 break; 519 case 1: 520 slrflag++; 521 altarg = (char *) 0; 522 break; 523 default: 524 break; 525 } 526 return((char *)0); 527 } 528 529 #define HELPINDENT (sizeof ("directory")) 530 531 /* 532 * Help command. 533 * Call each command handler with argc == 0 and argv[0] == name. 534 */ 535 void help(int argc, const char *argv[]) 536 { 537 extern struct cmd cmdtab[]; 538 struct cmd *c; 539 540 if (argc == 1) { 541 register int i, j, w, k; 542 int columns, width = 0, lines; 543 extern int NCMDS; 544 545 printf("Commands may be abbreviated. Commands are:\n\n"); 546 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 547 int len = strlen(c->c_name); 548 549 if (len > width) 550 width = len; 551 } 552 width = (width + 8) &~ 7; 553 columns = 80 / width; 554 if (columns == 0) 555 columns = 1; 556 lines = (NCMDS + columns - 1) / columns; 557 for (i = 0; i < lines; i++) { 558 for (j = 0; j < columns; j++) { 559 c = cmdtab + j * lines + i; 560 if (c->c_name && (!proxy || c->c_proxy)) { 561 printf("%s", c->c_name); 562 } 563 else if (c->c_name) { 564 for (k=0; k < (int) strlen(c->c_name); k++) { 565 (void) putchar(' '); 566 } 567 } 568 if (c + lines >= &cmdtab[NCMDS]) { 569 printf("\n"); 570 break; 571 } 572 w = strlen(c->c_name); 573 while (w < width) { 574 w = (w + 8) &~ 7; 575 (void) putchar('\t'); 576 } 577 } 578 } 579 (void) fflush(stdout); 580 return; 581 } 582 while (--argc > 0) { 583 const char *arg; 584 arg = *++argv; 585 c = getcmd(arg); 586 if (c == (struct cmd *)-1) 587 printf("?Ambiguous help command %s\n", arg); 588 else if (c == (struct cmd *)0) 589 printf("?Invalid help command %s\n", arg); 590 else 591 printf("%-*s\t%s\n", (int)HELPINDENT, 592 c->c_name, c->c_help); 593 } 594 (void) fflush(stdout); 595 } 596