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