1 /* $NetBSD: main.c,v 1.30 1998/01/21 11:14:34 lukem 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.30 1998/01/21 11:14:34 lukem 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 > MAX_IN_PORT_T || *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 > MAX_IN_PORT_T || *ep != '\0') 198 warnx("bad port number: %s (ignored)", optarg); 199 else 200 ftpport = htons((in_port_t)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 c = getcmd(margv[0]); 395 if (c == (struct cmd *)-1) { 396 puts("?Ambiguous command."); 397 continue; 398 } 399 if (c == NULL) { 400 #if !defined(SMALL) 401 /* 402 * attempt to el_parse() unknown commands. 403 * any command containing a ':' would be parsed 404 * as "[prog:]cmd ...", and will result in a 405 * false positive if prog != "ftp", so treat 406 * such commands as invalid. 407 */ 408 if (strchr(margv[0], ':') != NULL || 409 el_parse(el, margc, margv) != 0) 410 #endif /* !SMALL */ 411 puts("?Invalid command."); 412 continue; 413 } 414 if (c->c_conn && !connected) { 415 puts("Not connected."); 416 continue; 417 } 418 confirmrest = 0; 419 (*c->c_handler)(margc, margv); 420 if (bell && c->c_bell) 421 (void)putchar('\007'); 422 if (c->c_handler != help) 423 break; 424 } 425 (void)signal(SIGINT, (sig_t)intr); 426 (void)signal(SIGPIPE, (sig_t)lostpeer); 427 } 428 429 struct cmd * 430 getcmd(name) 431 const char *name; 432 { 433 const char *p, *q; 434 struct cmd *c, *found; 435 int nmatches, longest; 436 437 if (name == NULL) 438 return (0); 439 440 longest = 0; 441 nmatches = 0; 442 found = 0; 443 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 444 for (q = name; *q == *p++; q++) 445 if (*q == 0) /* exact match? */ 446 return (c); 447 if (!*q) { /* the name was a prefix */ 448 if (q - name > longest) { 449 longest = q - name; 450 nmatches = 1; 451 found = c; 452 } else if (q - name == longest) 453 nmatches++; 454 } 455 } 456 if (nmatches > 1) 457 return ((struct cmd *)-1); 458 return (found); 459 } 460 461 /* 462 * Slice a string up into argc/argv. 463 */ 464 465 int slrflag; 466 467 void 468 makeargv() 469 { 470 char *argp; 471 472 stringbase = line; /* scan from first of buffer */ 473 argbase = argbuf; /* store from first of buffer */ 474 slrflag = 0; 475 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 476 for (margc = 0; ; margc++) { 477 argp = slurpstring(); 478 sl_add(marg_sl, argp); 479 if (argp == NULL) 480 break; 481 } 482 #ifndef SMALL 483 if (cursor_pos == line) { 484 cursor_argc = 0; 485 cursor_argo = 0; 486 } else if (cursor_pos != NULL) { 487 cursor_argc = margc; 488 cursor_argo = strlen(margv[margc-1]); 489 } 490 #endif /* !SMALL */ 491 } 492 493 #ifdef SMALL 494 #define INC_CHKCURSOR(x) (x)++ 495 #else /* !SMALL */ 496 #define INC_CHKCURSOR(x) { (x)++ ; \ 497 if (x == cursor_pos) { \ 498 cursor_argc = margc; \ 499 cursor_argo = ap-argbase; \ 500 cursor_pos = NULL; \ 501 } } 502 503 #endif /* !SMALL */ 504 505 /* 506 * Parse string into argbuf; 507 * implemented with FSM to 508 * handle quoting and strings 509 */ 510 char * 511 slurpstring() 512 { 513 int got_one = 0; 514 char *sb = stringbase; 515 char *ap = argbase; 516 char *tmp = argbase; /* will return this if token found */ 517 518 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 519 switch (slrflag) { /* and $ as token for macro invoke */ 520 case 0: 521 slrflag++; 522 INC_CHKCURSOR(stringbase); 523 return ((*sb == '!') ? "!" : "$"); 524 /* NOTREACHED */ 525 case 1: 526 slrflag++; 527 altarg = stringbase; 528 break; 529 default: 530 break; 531 } 532 } 533 534 S0: 535 switch (*sb) { 536 537 case '\0': 538 goto OUT; 539 540 case ' ': 541 case '\t': 542 INC_CHKCURSOR(sb); 543 goto S0; 544 545 default: 546 switch (slrflag) { 547 case 0: 548 slrflag++; 549 break; 550 case 1: 551 slrflag++; 552 altarg = sb; 553 break; 554 default: 555 break; 556 } 557 goto S1; 558 } 559 560 S1: 561 switch (*sb) { 562 563 case ' ': 564 case '\t': 565 case '\0': 566 goto OUT; /* end of token */ 567 568 case '\\': 569 INC_CHKCURSOR(sb); 570 goto S2; /* slurp next character */ 571 572 case '"': 573 INC_CHKCURSOR(sb); 574 goto S3; /* slurp quoted string */ 575 576 default: 577 *ap = *sb; /* add character to token */ 578 ap++; 579 INC_CHKCURSOR(sb); 580 got_one = 1; 581 goto S1; 582 } 583 584 S2: 585 switch (*sb) { 586 587 case '\0': 588 goto OUT; 589 590 default: 591 *ap = *sb; 592 ap++; 593 INC_CHKCURSOR(sb); 594 got_one = 1; 595 goto S1; 596 } 597 598 S3: 599 switch (*sb) { 600 601 case '\0': 602 goto OUT; 603 604 case '"': 605 INC_CHKCURSOR(sb); 606 goto S1; 607 608 default: 609 *ap = *sb; 610 ap++; 611 INC_CHKCURSOR(sb); 612 got_one = 1; 613 goto S3; 614 } 615 616 OUT: 617 if (got_one) 618 *ap++ = '\0'; 619 argbase = ap; /* update storage pointer */ 620 stringbase = sb; /* update scan pointer */ 621 if (got_one) { 622 return (tmp); 623 } 624 switch (slrflag) { 625 case 0: 626 slrflag++; 627 break; 628 case 1: 629 slrflag++; 630 altarg = NULL; 631 break; 632 default: 633 break; 634 } 635 return (NULL); 636 } 637 638 /* 639 * Help command. 640 * Call each command handler with argc == 0 and argv[0] == name. 641 */ 642 void 643 help(argc, argv) 644 int argc; 645 char *argv[]; 646 { 647 struct cmd *c; 648 649 if (argc == 1) { 650 StringList *buf; 651 652 buf = sl_init(); 653 printf("%sommands may be abbreviated. Commands are:\n\n", 654 proxy ? "Proxy c" : "C"); 655 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) 656 if (c->c_name && (!proxy || c->c_proxy)) 657 sl_add(buf, c->c_name); 658 list_vertical(buf); 659 sl_free(buf, 0); 660 return; 661 } 662 663 #define HELPINDENT ((int) sizeof("disconnect")) 664 665 while (--argc > 0) { 666 char *arg; 667 668 arg = *++argv; 669 c = getcmd(arg); 670 if (c == (struct cmd *)-1) 671 printf("?Ambiguous help command %s\n", arg); 672 else if (c == NULL) 673 printf("?Invalid help command %s\n", arg); 674 else 675 printf("%-*s\t%s\n", HELPINDENT, 676 c->c_name, c->c_help); 677 } 678 } 679 680 void 681 usage() 682 { 683 (void)fprintf(stderr, 684 "usage: %s [-adeginptvV] [host [port]]\n" 685 " %s host:path[/]\n" 686 " %s ftp://host[:port]/path[/]\n" 687 " %s http://host[:port]/file\n", 688 __progname, __progname, __progname, __progname); 689 exit(1); 690 } 691