1 /* $NetBSD: main.c,v 1.26 1997/10/14 16:31:22 christos 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.26 1997/10/14 16:31:22 christos 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 if (isatty(fileno(stdout)) && !dumbterm) 158 progress = 1; /* progress bar on if tty is usable */ 159 160 while ((ch = getopt(argc, argv, "adeginpP:tvV")) != -1) { 161 switch (ch) { 162 case 'a': 163 anonftp = 1; 164 break; 165 166 case 'd': 167 options |= SO_DEBUG; 168 debug++; 169 break; 170 171 case 'e': 172 #ifndef SMALL 173 editing = 0; 174 #endif 175 break; 176 177 case 'g': 178 doglob = 0; 179 break; 180 181 case 'i': 182 interactive = 0; 183 break; 184 185 case 'n': 186 autologin = 0; 187 break; 188 189 case 'p': 190 passivemode = 1; 191 break; 192 193 case 'P': 194 port = strtol(optarg, &ep, 10); 195 if (port < 1 || port > 0xffff || *ep != '\0') 196 warnx("bad port number: %s (ignored)", optarg); 197 else 198 ftpport = htons(port); 199 break; 200 201 case 't': 202 trace = 1; 203 break; 204 205 case 'v': 206 verbose = 1; 207 break; 208 209 case 'V': 210 verbose = 0; 211 break; 212 213 default: 214 usage(); 215 } 216 } 217 argc -= optind; 218 argv += optind; 219 220 cpend = 0; /* no pending replies */ 221 proxy = 0; /* proxy not active */ 222 crflag = 1; /* strip c.r. on ascii gets */ 223 sendport = -1; /* not using ports */ 224 /* 225 * Set up the home directory in case we're globbing. 226 */ 227 cp = getlogin(); 228 if (cp != NULL) { 229 pw = getpwnam(cp); 230 } 231 if (pw == NULL) 232 pw = getpwuid(getuid()); 233 if (pw != NULL) { 234 home = homedir; 235 (void)strcpy(home, pw->pw_dir); 236 } 237 238 setttywidth(0); 239 (void)signal(SIGWINCH, setttywidth); 240 241 #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ 242 (void)&argc; 243 (void)&argv; 244 #endif 245 246 if (argc > 0) { 247 if (strchr(argv[0], ':') != NULL) { 248 anonftp = 1; /* Handle "automatic" transfers. */ 249 rval = auto_fetch(argc, argv); 250 if (rval >= 0) /* -1 == connected and cd-ed */ 251 exit(rval); 252 } else { 253 char *xargv[5]; 254 255 if (setjmp(toplevel)) 256 exit(0); 257 (void)signal(SIGINT, (sig_t)intr); 258 (void)signal(SIGPIPE, (sig_t)lostpeer); 259 xargv[0] = __progname; 260 xargv[1] = argv[0]; 261 xargv[2] = argv[1]; 262 xargv[3] = argv[2]; 263 xargv[4] = NULL; 264 setpeer(argc+1, xargv); 265 } 266 } 267 #ifndef SMALL 268 controlediting(); 269 #endif /* !SMALL */ 270 top = setjmp(toplevel) == 0; 271 if (top) { 272 (void)signal(SIGINT, (sig_t)intr); 273 (void)signal(SIGPIPE, (sig_t)lostpeer); 274 } 275 for (;;) { 276 cmdscanner(top); 277 top = 1; 278 } 279 } 280 281 void 282 intr() 283 { 284 285 alarmtimer(0); 286 longjmp(toplevel, 1); 287 } 288 289 void 290 lostpeer() 291 { 292 293 alarmtimer(0); 294 if (connected) { 295 if (cout != NULL) { 296 (void)shutdown(fileno(cout), 1+1); 297 (void)fclose(cout); 298 cout = NULL; 299 } 300 if (data >= 0) { 301 (void)shutdown(data, 1+1); 302 (void)close(data); 303 data = -1; 304 } 305 connected = 0; 306 } 307 pswitch(1); 308 if (connected) { 309 if (cout != NULL) { 310 (void)shutdown(fileno(cout), 1+1); 311 (void)fclose(cout); 312 cout = NULL; 313 } 314 connected = 0; 315 } 316 proxflag = 0; 317 pswitch(0); 318 } 319 320 /* 321 * Generate a prompt 322 */ 323 char * 324 prompt() 325 { 326 return ("ftp> "); 327 } 328 329 /* 330 * Command parser. 331 */ 332 void 333 cmdscanner(top) 334 int top; 335 { 336 struct cmd *c; 337 int num; 338 339 if (!top 340 #ifndef SMALL 341 && !editing 342 #endif /* !SMALL */ 343 ) 344 (void)putchar('\n'); 345 for (;;) { 346 #ifndef SMALL 347 if (!editing) { 348 #endif /* !SMALL */ 349 if (fromatty) { 350 fputs(prompt(), stdout); 351 (void)fflush(stdout); 352 } 353 if (fgets(line, sizeof(line), stdin) == NULL) 354 quit(0, 0); 355 num = strlen(line); 356 if (num == 0) 357 break; 358 if (line[--num] == '\n') { 359 if (num == 0) 360 break; 361 line[num] = '\0'; 362 } else if (num == sizeof(line) - 2) { 363 puts("sorry, input line too long."); 364 while ((num = getchar()) != '\n' && num != EOF) 365 /* void */; 366 break; 367 } /* else it was a line without a newline */ 368 #ifndef SMALL 369 } else { 370 const char *buf; 371 HistEvent ev; 372 cursor_pos = NULL; 373 374 if ((buf = el_gets(el, &num)) == NULL || num == 0) 375 quit(0, 0); 376 if (line[--num] == '\n') { 377 if (num == 0) 378 break; 379 } else if (num >= sizeof(line)) { 380 puts("sorry, input line too long."); 381 break; 382 } 383 memcpy(line, buf, num); 384 line[num] = '\0'; 385 history(hist, &ev, H_ENTER, buf); 386 } 387 #endif /* !SMALL */ 388 389 makeargv(); 390 if (margc == 0) 391 continue; 392 #if 0 && !defined(SMALL) /* XXX: don't want el_parse */ 393 /* 394 * el_parse returns -1 to signal that it's not been handled 395 * internally. 396 */ 397 if (el_parse(el, margc, margv) != -1) 398 continue; 399 #endif /* !SMALL */ 400 c = getcmd(margv[0]); 401 if (c == (struct cmd *)-1) { 402 puts("?Ambiguous command."); 403 continue; 404 } 405 if (c == 0) { 406 puts("?Invalid command."); 407 continue; 408 } 409 if (c->c_conn && !connected) { 410 puts("Not connected."); 411 continue; 412 } 413 confirmrest = 0; 414 (*c->c_handler)(margc, margv); 415 if (bell && c->c_bell) 416 (void)putchar('\007'); 417 if (c->c_handler != help) 418 break; 419 } 420 (void)signal(SIGINT, (sig_t)intr); 421 (void)signal(SIGPIPE, (sig_t)lostpeer); 422 } 423 424 struct cmd * 425 getcmd(name) 426 const char *name; 427 { 428 const char *p, *q; 429 struct cmd *c, *found; 430 int nmatches, longest; 431 432 if (name == NULL) 433 return (0); 434 435 longest = 0; 436 nmatches = 0; 437 found = 0; 438 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 439 for (q = name; *q == *p++; q++) 440 if (*q == 0) /* exact match? */ 441 return (c); 442 if (!*q) { /* the name was a prefix */ 443 if (q - name > longest) { 444 longest = q - name; 445 nmatches = 1; 446 found = c; 447 } else if (q - name == longest) 448 nmatches++; 449 } 450 } 451 if (nmatches > 1) 452 return ((struct cmd *)-1); 453 return (found); 454 } 455 456 /* 457 * Slice a string up into argc/argv. 458 */ 459 460 int slrflag; 461 462 void 463 makeargv() 464 { 465 char *argp; 466 467 stringbase = line; /* scan from first of buffer */ 468 argbase = argbuf; /* store from first of buffer */ 469 slrflag = 0; 470 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 471 for (margc = 0; ; margc++) { 472 argp = slurpstring(); 473 sl_add(marg_sl, argp); 474 if (argp == NULL) 475 break; 476 } 477 #ifndef SMALL 478 if (cursor_pos == line) { 479 cursor_argc = 0; 480 cursor_argo = 0; 481 } else if (cursor_pos != NULL) { 482 cursor_argc = margc; 483 cursor_argo = strlen(margv[margc-1]); 484 } 485 #endif /* !SMALL */ 486 } 487 488 #ifdef SMALL 489 #define INC_CHKCURSOR(x) (x)++ 490 #else /* !SMALL */ 491 #define INC_CHKCURSOR(x) { (x)++ ; \ 492 if (x == cursor_pos) { \ 493 cursor_argc = margc; \ 494 cursor_argo = ap-argbase; \ 495 cursor_pos = NULL; \ 496 } } 497 498 #endif /* !SMALL */ 499 500 /* 501 * Parse string into argbuf; 502 * implemented with FSM to 503 * handle quoting and strings 504 */ 505 char * 506 slurpstring() 507 { 508 int got_one = 0; 509 char *sb = stringbase; 510 char *ap = argbase; 511 char *tmp = argbase; /* will return this if token found */ 512 513 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 514 switch (slrflag) { /* and $ as token for macro invoke */ 515 case 0: 516 slrflag++; 517 INC_CHKCURSOR(stringbase); 518 return ((*sb == '!') ? "!" : "$"); 519 /* NOTREACHED */ 520 case 1: 521 slrflag++; 522 altarg = stringbase; 523 break; 524 default: 525 break; 526 } 527 } 528 529 S0: 530 switch (*sb) { 531 532 case '\0': 533 goto OUT; 534 535 case ' ': 536 case '\t': 537 INC_CHKCURSOR(sb); 538 goto S0; 539 540 default: 541 switch (slrflag) { 542 case 0: 543 slrflag++; 544 break; 545 case 1: 546 slrflag++; 547 altarg = sb; 548 break; 549 default: 550 break; 551 } 552 goto S1; 553 } 554 555 S1: 556 switch (*sb) { 557 558 case ' ': 559 case '\t': 560 case '\0': 561 goto OUT; /* end of token */ 562 563 case '\\': 564 INC_CHKCURSOR(sb); 565 goto S2; /* slurp next character */ 566 567 case '"': 568 INC_CHKCURSOR(sb); 569 goto S3; /* slurp quoted string */ 570 571 default: 572 *ap = *sb; /* add character to token */ 573 ap++; 574 INC_CHKCURSOR(sb); 575 got_one = 1; 576 goto S1; 577 } 578 579 S2: 580 switch (*sb) { 581 582 case '\0': 583 goto OUT; 584 585 default: 586 *ap = *sb; 587 ap++; 588 INC_CHKCURSOR(sb); 589 got_one = 1; 590 goto S1; 591 } 592 593 S3: 594 switch (*sb) { 595 596 case '\0': 597 goto OUT; 598 599 case '"': 600 INC_CHKCURSOR(sb); 601 goto S1; 602 603 default: 604 *ap = *sb; 605 ap++; 606 INC_CHKCURSOR(sb); 607 got_one = 1; 608 goto S3; 609 } 610 611 OUT: 612 if (got_one) 613 *ap++ = '\0'; 614 argbase = ap; /* update storage pointer */ 615 stringbase = sb; /* update scan pointer */ 616 if (got_one) { 617 return (tmp); 618 } 619 switch (slrflag) { 620 case 0: 621 slrflag++; 622 break; 623 case 1: 624 slrflag++; 625 altarg = (char *) 0; 626 break; 627 default: 628 break; 629 } 630 return ((char *)0); 631 } 632 633 /* 634 * Help command. 635 * Call each command handler with argc == 0 and argv[0] == name. 636 */ 637 void 638 help(argc, argv) 639 int argc; 640 char *argv[]; 641 { 642 struct cmd *c; 643 644 if (argc == 1) { 645 StringList *buf; 646 647 buf = sl_init(); 648 printf("%sommands may be abbreviated. Commands are:\n\n", 649 proxy ? "Proxy c" : "C"); 650 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) 651 if (c->c_name && (!proxy || c->c_proxy)) 652 sl_add(buf, c->c_name); 653 list_vertical(buf); 654 sl_free(buf, 0); 655 return; 656 } 657 658 #define HELPINDENT ((int) sizeof("disconnect")) 659 660 while (--argc > 0) { 661 char *arg; 662 663 arg = *++argv; 664 c = getcmd(arg); 665 if (c == (struct cmd *)-1) 666 printf("?Ambiguous help command %s\n", arg); 667 else if (c == (struct cmd *)0) 668 printf("?Invalid help command %s\n", arg); 669 else 670 printf("%-*s\t%s\n", HELPINDENT, 671 c->c_name, c->c_help); 672 } 673 } 674 675 void 676 usage() 677 { 678 (void)fprintf(stderr, 679 "usage: %s [-adeginptvV] [host [port]]\n" 680 " %s host:path[/]\n" 681 " %s ftp://host[:port]/path[/]\n" 682 " %s http://host[:port]/file\n", 683 __progname, __progname, __progname, __progname); 684 exit(1); 685 } 686