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