1 /* $NetBSD: main.c,v 1.27 1997/12/12 23:34:56 gwr Exp $ */ 2 3 /* 4 * Copyright (c) 1985, 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; 45 #else 46 __RCSID("$NetBSD: main.c,v 1.27 1997/12/12 23:34:56 gwr Exp $"); 47 #endif 48 #endif /* not lint */ 49 50 /* 51 * FTP User Program -- Command Interface. 52 */ 53 #include <sys/types.h> 54 #include <sys/socket.h> 55 56 #include <err.h> 57 #include <netdb.h> 58 #include <pwd.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 #include "ftp_var.h" 65 #include "pathnames.h" 66 67 int main __P((int, char **)); 68 69 int 70 main(argc, argv) 71 int argc; 72 char *argv[]; 73 { 74 struct servent *sp; 75 int ch, top, rval; 76 long port; 77 struct passwd *pw = NULL; 78 char *cp, *ep, homedir[MAXPATHLEN]; 79 int dumbterm; 80 81 sp = getservbyname("ftp", "tcp"); 82 if (sp == 0) 83 ftpport = htons(FTP_PORT); /* good fallback */ 84 else 85 ftpport = sp->s_port; 86 sp = getservbyname("http", "tcp"); 87 if (sp == 0) 88 httpport = htons(HTTP_PORT); /* good fallback */ 89 else 90 httpport = sp->s_port; 91 gateport = 0; 92 cp = getenv("FTPSERVERPORT"); 93 if (cp != NULL) { 94 port = strtol(cp, &ep, 10); 95 if (port < 1 || port > 0xffff || *ep != '\0') 96 warnx("bad FTPSERVERPORT port number: %s (ignored)", 97 cp); 98 else 99 gateport = htons(port); 100 } 101 if (gateport == 0) { 102 sp = getservbyname("ftpgate", "tcp"); 103 if (sp == 0) 104 gateport = htons(GATE_PORT); 105 else 106 gateport = sp->s_port; 107 } 108 doglob = 1; 109 interactive = 1; 110 autologin = 1; 111 passivemode = 0; 112 preserve = 1; 113 verbose = 0; 114 progress = 0; 115 gatemode = 0; 116 #ifndef SMALL 117 editing = 0; 118 el = NULL; 119 hist = NULL; 120 #endif 121 mark = HASHBYTES; 122 marg_sl = sl_init(); 123 if ((tmpdir = getenv("TMPDIR")) == NULL) 124 tmpdir = _PATH_TMP; 125 126 cp = strrchr(argv[0], '/'); 127 cp = (cp == NULL) ? argv[0] : cp + 1; 128 if (strcmp(cp, "pftp") == 0) 129 passivemode = 1; 130 else if (strcmp(cp, "gate-ftp") == 0) 131 gatemode = 1; 132 133 gateserver = getenv("FTPSERVER"); 134 if (gateserver == NULL || *gateserver == '\0') 135 gateserver = GATE_SERVER; 136 if (gatemode) { 137 if (*gateserver == '\0') { 138 warnx( 139 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); 140 gatemode = 0; 141 } 142 } 143 144 cp = getenv("TERM"); 145 if (cp == NULL || strcmp(cp, "dumb") == 0) 146 dumbterm = 1; 147 else 148 dumbterm = 0; 149 fromatty = isatty(fileno(stdin)); 150 if (fromatty) { 151 verbose = 1; /* verbose if from a tty */ 152 #ifndef SMALL 153 if (! dumbterm) 154 editing = 1; /* editing mode on if tty is usable */ 155 #endif 156 } 157 #ifndef SMALL 158 if (isatty(fileno(stdout)) && !dumbterm) 159 progress = 1; /* progress bar on if tty is usable */ 160 #endif 161 162 while ((ch = getopt(argc, argv, "adeginpP:tvV")) != -1) { 163 switch (ch) { 164 case 'a': 165 anonftp = 1; 166 break; 167 168 case 'd': 169 options |= SO_DEBUG; 170 debug++; 171 break; 172 173 case 'e': 174 #ifndef SMALL 175 editing = 0; 176 #endif 177 break; 178 179 case 'g': 180 doglob = 0; 181 break; 182 183 case 'i': 184 interactive = 0; 185 break; 186 187 case 'n': 188 autologin = 0; 189 break; 190 191 case 'p': 192 passivemode = 1; 193 break; 194 195 case 'P': 196 port = strtol(optarg, &ep, 10); 197 if (port < 1 || port > 0xffff || *ep != '\0') 198 warnx("bad port number: %s (ignored)", optarg); 199 else 200 ftpport = htons(port); 201 break; 202 203 case 't': 204 trace = 1; 205 break; 206 207 case 'v': 208 verbose = 1; 209 break; 210 211 case 'V': 212 verbose = 0; 213 break; 214 215 default: 216 usage(); 217 } 218 } 219 argc -= optind; 220 argv += optind; 221 222 cpend = 0; /* no pending replies */ 223 proxy = 0; /* proxy not active */ 224 crflag = 1; /* strip c.r. on ascii gets */ 225 sendport = -1; /* not using ports */ 226 /* 227 * Set up the home directory in case we're globbing. 228 */ 229 cp = getlogin(); 230 if (cp != NULL) { 231 pw = getpwnam(cp); 232 } 233 if (pw == NULL) 234 pw = getpwuid(getuid()); 235 if (pw != NULL) { 236 home = homedir; 237 (void)strcpy(home, pw->pw_dir); 238 } 239 240 setttywidth(0); 241 (void)signal(SIGWINCH, setttywidth); 242 243 #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ 244 (void)&argc; 245 (void)&argv; 246 #endif 247 248 if (argc > 0) { 249 if (strchr(argv[0], ':') != NULL) { 250 anonftp = 1; /* Handle "automatic" transfers. */ 251 rval = auto_fetch(argc, argv); 252 if (rval >= 0) /* -1 == connected and cd-ed */ 253 exit(rval); 254 } else { 255 char *xargv[5]; 256 257 if (setjmp(toplevel)) 258 exit(0); 259 (void)signal(SIGINT, (sig_t)intr); 260 (void)signal(SIGPIPE, (sig_t)lostpeer); 261 xargv[0] = __progname; 262 xargv[1] = argv[0]; 263 xargv[2] = argv[1]; 264 xargv[3] = argv[2]; 265 xargv[4] = NULL; 266 setpeer(argc+1, xargv); 267 } 268 } 269 #ifndef SMALL 270 controlediting(); 271 #endif /* !SMALL */ 272 top = setjmp(toplevel) == 0; 273 if (top) { 274 (void)signal(SIGINT, (sig_t)intr); 275 (void)signal(SIGPIPE, (sig_t)lostpeer); 276 } 277 for (;;) { 278 cmdscanner(top); 279 top = 1; 280 } 281 } 282 283 void 284 intr() 285 { 286 287 alarmtimer(0); 288 longjmp(toplevel, 1); 289 } 290 291 void 292 lostpeer() 293 { 294 295 alarmtimer(0); 296 if (connected) { 297 if (cout != NULL) { 298 (void)shutdown(fileno(cout), 1+1); 299 (void)fclose(cout); 300 cout = NULL; 301 } 302 if (data >= 0) { 303 (void)shutdown(data, 1+1); 304 (void)close(data); 305 data = -1; 306 } 307 connected = 0; 308 } 309 pswitch(1); 310 if (connected) { 311 if (cout != NULL) { 312 (void)shutdown(fileno(cout), 1+1); 313 (void)fclose(cout); 314 cout = NULL; 315 } 316 connected = 0; 317 } 318 proxflag = 0; 319 pswitch(0); 320 } 321 322 /* 323 * Generate a prompt 324 */ 325 char * 326 prompt() 327 { 328 return ("ftp> "); 329 } 330 331 /* 332 * Command parser. 333 */ 334 void 335 cmdscanner(top) 336 int top; 337 { 338 struct cmd *c; 339 int num; 340 341 if (!top 342 #ifndef SMALL 343 && !editing 344 #endif /* !SMALL */ 345 ) 346 (void)putchar('\n'); 347 for (;;) { 348 #ifndef SMALL 349 if (!editing) { 350 #endif /* !SMALL */ 351 if (fromatty) { 352 fputs(prompt(), stdout); 353 (void)fflush(stdout); 354 } 355 if (fgets(line, sizeof(line), stdin) == NULL) 356 quit(0, 0); 357 num = strlen(line); 358 if (num == 0) 359 break; 360 if (line[--num] == '\n') { 361 if (num == 0) 362 break; 363 line[num] = '\0'; 364 } else if (num == sizeof(line) - 2) { 365 puts("sorry, input line too long."); 366 while ((num = getchar()) != '\n' && num != EOF) 367 /* void */; 368 break; 369 } /* else it was a line without a newline */ 370 #ifndef SMALL 371 } else { 372 const char *buf; 373 HistEvent ev; 374 cursor_pos = NULL; 375 376 if ((buf = el_gets(el, &num)) == NULL || num == 0) 377 quit(0, 0); 378 if (line[--num] == '\n') { 379 if (num == 0) 380 break; 381 } else if (num >= sizeof(line)) { 382 puts("sorry, input line too long."); 383 break; 384 } 385 memcpy(line, buf, num); 386 line[num] = '\0'; 387 history(hist, &ev, H_ENTER, buf); 388 } 389 #endif /* !SMALL */ 390 391 makeargv(); 392 if (margc == 0) 393 continue; 394 #if 0 && !defined(SMALL) /* XXX: don't want el_parse */ 395 /* 396 * el_parse returns -1 to signal that it's not been handled 397 * internally. 398 */ 399 if (el_parse(el, margc, margv) != -1) 400 continue; 401 #endif /* !SMALL */ 402 c = getcmd(margv[0]); 403 if (c == (struct cmd *)-1) { 404 puts("?Ambiguous command."); 405 continue; 406 } 407 if (c == 0) { 408 puts("?Invalid command."); 409 continue; 410 } 411 if (c->c_conn && !connected) { 412 puts("Not connected."); 413 continue; 414 } 415 confirmrest = 0; 416 (*c->c_handler)(margc, margv); 417 if (bell && c->c_bell) 418 (void)putchar('\007'); 419 if (c->c_handler != help) 420 break; 421 } 422 (void)signal(SIGINT, (sig_t)intr); 423 (void)signal(SIGPIPE, (sig_t)lostpeer); 424 } 425 426 struct cmd * 427 getcmd(name) 428 const char *name; 429 { 430 const char *p, *q; 431 struct cmd *c, *found; 432 int nmatches, longest; 433 434 if (name == NULL) 435 return (0); 436 437 longest = 0; 438 nmatches = 0; 439 found = 0; 440 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 441 for (q = name; *q == *p++; q++) 442 if (*q == 0) /* exact match? */ 443 return (c); 444 if (!*q) { /* the name was a prefix */ 445 if (q - name > longest) { 446 longest = q - name; 447 nmatches = 1; 448 found = c; 449 } else if (q - name == longest) 450 nmatches++; 451 } 452 } 453 if (nmatches > 1) 454 return ((struct cmd *)-1); 455 return (found); 456 } 457 458 /* 459 * Slice a string up into argc/argv. 460 */ 461 462 int slrflag; 463 464 void 465 makeargv() 466 { 467 char *argp; 468 469 stringbase = line; /* scan from first of buffer */ 470 argbase = argbuf; /* store from first of buffer */ 471 slrflag = 0; 472 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 473 for (margc = 0; ; margc++) { 474 argp = slurpstring(); 475 sl_add(marg_sl, argp); 476 if (argp == NULL) 477 break; 478 } 479 #ifndef SMALL 480 if (cursor_pos == line) { 481 cursor_argc = 0; 482 cursor_argo = 0; 483 } else if (cursor_pos != NULL) { 484 cursor_argc = margc; 485 cursor_argo = strlen(margv[margc-1]); 486 } 487 #endif /* !SMALL */ 488 } 489 490 #ifdef SMALL 491 #define INC_CHKCURSOR(x) (x)++ 492 #else /* !SMALL */ 493 #define INC_CHKCURSOR(x) { (x)++ ; \ 494 if (x == cursor_pos) { \ 495 cursor_argc = margc; \ 496 cursor_argo = ap-argbase; \ 497 cursor_pos = NULL; \ 498 } } 499 500 #endif /* !SMALL */ 501 502 /* 503 * Parse string into argbuf; 504 * implemented with FSM to 505 * handle quoting and strings 506 */ 507 char * 508 slurpstring() 509 { 510 int got_one = 0; 511 char *sb = stringbase; 512 char *ap = argbase; 513 char *tmp = argbase; /* will return this if token found */ 514 515 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 516 switch (slrflag) { /* and $ as token for macro invoke */ 517 case 0: 518 slrflag++; 519 INC_CHKCURSOR(stringbase); 520 return ((*sb == '!') ? "!" : "$"); 521 /* NOTREACHED */ 522 case 1: 523 slrflag++; 524 altarg = stringbase; 525 break; 526 default: 527 break; 528 } 529 } 530 531 S0: 532 switch (*sb) { 533 534 case '\0': 535 goto OUT; 536 537 case ' ': 538 case '\t': 539 INC_CHKCURSOR(sb); 540 goto S0; 541 542 default: 543 switch (slrflag) { 544 case 0: 545 slrflag++; 546 break; 547 case 1: 548 slrflag++; 549 altarg = sb; 550 break; 551 default: 552 break; 553 } 554 goto S1; 555 } 556 557 S1: 558 switch (*sb) { 559 560 case ' ': 561 case '\t': 562 case '\0': 563 goto OUT; /* end of token */ 564 565 case '\\': 566 INC_CHKCURSOR(sb); 567 goto S2; /* slurp next character */ 568 569 case '"': 570 INC_CHKCURSOR(sb); 571 goto S3; /* slurp quoted string */ 572 573 default: 574 *ap = *sb; /* add character to token */ 575 ap++; 576 INC_CHKCURSOR(sb); 577 got_one = 1; 578 goto S1; 579 } 580 581 S2: 582 switch (*sb) { 583 584 case '\0': 585 goto OUT; 586 587 default: 588 *ap = *sb; 589 ap++; 590 INC_CHKCURSOR(sb); 591 got_one = 1; 592 goto S1; 593 } 594 595 S3: 596 switch (*sb) { 597 598 case '\0': 599 goto OUT; 600 601 case '"': 602 INC_CHKCURSOR(sb); 603 goto S1; 604 605 default: 606 *ap = *sb; 607 ap++; 608 INC_CHKCURSOR(sb); 609 got_one = 1; 610 goto S3; 611 } 612 613 OUT: 614 if (got_one) 615 *ap++ = '\0'; 616 argbase = ap; /* update storage pointer */ 617 stringbase = sb; /* update scan pointer */ 618 if (got_one) { 619 return (tmp); 620 } 621 switch (slrflag) { 622 case 0: 623 slrflag++; 624 break; 625 case 1: 626 slrflag++; 627 altarg = (char *) 0; 628 break; 629 default: 630 break; 631 } 632 return ((char *)0); 633 } 634 635 /* 636 * Help command. 637 * Call each command handler with argc == 0 and argv[0] == name. 638 */ 639 void 640 help(argc, argv) 641 int argc; 642 char *argv[]; 643 { 644 struct cmd *c; 645 646 if (argc == 1) { 647 StringList *buf; 648 649 buf = sl_init(); 650 printf("%sommands may be abbreviated. Commands are:\n\n", 651 proxy ? "Proxy c" : "C"); 652 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) 653 if (c->c_name && (!proxy || c->c_proxy)) 654 sl_add(buf, c->c_name); 655 list_vertical(buf); 656 sl_free(buf, 0); 657 return; 658 } 659 660 #define HELPINDENT ((int) sizeof("disconnect")) 661 662 while (--argc > 0) { 663 char *arg; 664 665 arg = *++argv; 666 c = getcmd(arg); 667 if (c == (struct cmd *)-1) 668 printf("?Ambiguous help command %s\n", arg); 669 else if (c == (struct cmd *)0) 670 printf("?Invalid help command %s\n", arg); 671 else 672 printf("%-*s\t%s\n", HELPINDENT, 673 c->c_name, c->c_help); 674 } 675 } 676 677 void 678 usage() 679 { 680 (void)fprintf(stderr, 681 "usage: %s [-adeginptvV] [host [port]]\n" 682 " %s host:path[/]\n" 683 " %s ftp://host[:port]/path[/]\n" 684 " %s http://host[:port]/file\n", 685 __progname, __progname, __progname, __progname); 686 exit(1); 687 } 688