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