1 /* $NetBSD: main.c,v 1.22 2021/04/25 07:50:37 lukem Exp $ */ 2 /* from NetBSD: main.c,v 1.127 2020/07/18 03:00:37 lukem Exp */ 3 4 /*- 5 * Copyright (c) 1996-2015 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Luke Mewburn. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF 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 /* 63 * Copyright (C) 1997 and 1998 WIDE Project. 64 * All rights reserved. 65 * 66 * Redistribution and use in source and binary forms, with or without 67 * modification, are permitted provided that the following conditions 68 * are met: 69 * 1. Redistributions of source code must retain the above copyright 70 * notice, this list of conditions and the following disclaimer. 71 * 2. Redistributions in binary form must reproduce the above copyright 72 * notice, this list of conditions and the following disclaimer in the 73 * documentation and/or other materials provided with the distribution. 74 * 3. Neither the name of the project nor the names of its contributors 75 * may be used to endorse or promote products derived from this software 76 * without specific prior written permission. 77 * 78 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 79 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 80 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 81 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 82 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 83 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 84 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 85 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 86 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 87 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 88 * SUCH DAMAGE. 89 */ 90 91 #include "tnftp.h" 92 93 #if 0 /* tnftp */ 94 95 #include <sys/cdefs.h> 96 #ifndef lint 97 __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\ 98 The Regents of the University of California. All rights reserved.\ 99 Copyright 1996-2015 The NetBSD Foundation, Inc. All rights reserved"); 100 #endif /* not lint */ 101 102 #ifndef lint 103 #if 0 104 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; 105 #else 106 __RCSID(" NetBSD: main.c,v 1.127 2020/07/18 03:00:37 lukem Exp "); 107 #endif 108 #endif /* not lint */ 109 110 /* 111 * FTP User Program -- Command Interface. 112 */ 113 #include <sys/types.h> 114 #include <sys/socket.h> 115 116 #include <err.h> 117 #include <errno.h> 118 #include <netdb.h> 119 #include <paths.h> 120 #include <pwd.h> 121 #include <signal.h> 122 #include <stdio.h> 123 #include <stdlib.h> 124 #include <string.h> 125 #include <time.h> 126 #include <unistd.h> 127 #include <locale.h> 128 129 #endif /* tnftp */ 130 131 #define GLOBAL /* force GLOBAL decls in ftp_var.h to be declared */ 132 #include "ftp_var.h" 133 134 #define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */ 135 #define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */ 136 #define HTTPS_PROXY "https_proxy" /* env var with HTTPS proxy location */ 137 #define NO_PROXY "no_proxy" /* env var with list of non-proxied 138 * hosts, comma or space separated */ 139 140 static int usage(void); 141 static int usage_help(void); 142 static void setupoption(const char *, const char *, const char *); 143 144 int 145 main(int volatile argc, char **volatile argv) 146 { 147 int ch, rval; 148 struct passwd *pw; 149 char *cp, *ep, *anonpass, *upload_path, *src_addr; 150 const char *anonuser; 151 int dumbterm, isupload; 152 size_t len; 153 154 tzset(); 155 #if defined(HAVE_SETLOCALE) 156 setlocale(LC_ALL, ""); 157 #endif 158 setprogname(argv[0]); 159 160 sigint_raised = 0; 161 162 ftpport = "ftp"; 163 httpport = "http"; 164 #ifdef WITH_SSL 165 httpsport = "https"; 166 #endif 167 gateport = NULL; 168 cp = getenv("FTPSERVERPORT"); 169 if (cp != NULL) 170 gateport = cp; 171 else 172 gateport = "ftpgate"; 173 doglob = 1; 174 interactive = 1; 175 autologin = 1; 176 passivemode = 1; 177 activefallback = 1; 178 preserve = 1; 179 verbose = 0; 180 progress = 0; 181 gatemode = 0; 182 data = -1; 183 outfile = NULL; 184 restartautofetch = 0; 185 #ifndef NO_EDITCOMPLETE 186 editing = 0; 187 el = NULL; 188 hist = NULL; 189 #endif 190 bytes = 0; 191 mark = HASHBYTES; 192 rate_get = 0; 193 rate_get_incr = DEFAULTINCR; 194 rate_put = 0; 195 rate_put_incr = DEFAULTINCR; 196 #ifdef INET6 197 epsv4 = 1; 198 epsv6 = 1; 199 #else 200 epsv4 = 0; 201 epsv6 = 0; 202 #endif 203 epsv4bad = 0; 204 epsv6bad = 0; 205 src_addr = NULL; 206 upload_path = NULL; 207 isupload = 0; 208 reply_callback = NULL; 209 #ifdef INET6 210 family = AF_UNSPEC; 211 #else 212 family = AF_INET; /* force AF_INET if no INET6 support */ 213 #endif 214 215 netrc[0] = '\0'; 216 cp = getenv("NETRC"); 217 if (cp != NULL && strlcpy(netrc, cp, sizeof(netrc)) >= sizeof(netrc)) 218 errx(1, "$NETRC `%s': %s", cp, strerror(ENAMETOOLONG)); 219 220 marg_sl = ftp_sl_init(); 221 if ((tmpdir = getenv("TMPDIR")) == NULL) 222 tmpdir = _PATH_TMP; 223 224 /* Set default operation mode based on FTPMODE environment variable */ 225 if ((cp = getenv("FTPMODE")) != NULL) { 226 if (strcasecmp(cp, "passive") == 0) { 227 passivemode = 1; 228 activefallback = 0; 229 } else if (strcasecmp(cp, "active") == 0) { 230 passivemode = 0; 231 activefallback = 0; 232 } else if (strcasecmp(cp, "gate") == 0) { 233 gatemode = 1; 234 } else if (strcasecmp(cp, "auto") == 0) { 235 passivemode = 1; 236 activefallback = 1; 237 } else 238 warnx("Unknown $FTPMODE `%s'; using defaults", cp); 239 } 240 241 if (strcmp(getprogname(), "pftp") == 0) { 242 passivemode = 1; 243 activefallback = 0; 244 } else if (strcmp(getprogname(), "gate-ftp") == 0) 245 gatemode = 1; 246 247 gateserver = getenv("FTPSERVER"); 248 if (gateserver == NULL || *gateserver == '\0') 249 gateserver = GATE_SERVER; 250 if (gatemode) { 251 if (*gateserver == '\0') { 252 warnx( 253 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); 254 gatemode = 0; 255 } 256 } 257 258 cp = getenv("TERM"); 259 if (cp == NULL || strcmp(cp, "dumb") == 0) 260 dumbterm = 1; 261 else 262 dumbterm = 0; 263 fromatty = isatty(fileno(stdin)); 264 ttyout = stdout; 265 if (isatty(fileno(ttyout))) { 266 verbose = 1; /* verbose if to a tty */ 267 if (! dumbterm) { 268 #ifndef NO_EDITCOMPLETE 269 if (fromatty) /* editing mode on if tty is usable */ 270 editing = 1; 271 #endif 272 #ifndef NO_PROGRESS 273 if (foregroundproc()) 274 progress = 1; /* progress bar on if fg */ 275 #endif 276 } 277 } 278 279 while ((ch = getopt(argc, argv, "?46AadefginN:o:pP:q:r:Rs:tT:u:vVx:")) != -1) { 280 switch (ch) { 281 case '4': 282 family = AF_INET; 283 break; 284 285 case '6': 286 #ifdef INET6 287 family = AF_INET6; 288 #else 289 warnx("INET6 support is not available; ignoring -6"); 290 #endif 291 break; 292 293 case 'A': 294 activefallback = 0; 295 passivemode = 0; 296 break; 297 298 case 'a': 299 anonftp = 1; 300 break; 301 302 case 'd': 303 options |= SO_DEBUG; 304 ftp_debug++; 305 break; 306 307 case 'e': 308 #ifndef NO_EDITCOMPLETE 309 editing = 0; 310 #endif 311 break; 312 313 case 'f': 314 flushcache = 1; 315 break; 316 317 case 'g': 318 doglob = 0; 319 break; 320 321 case 'i': 322 interactive = 0; 323 break; 324 325 case 'n': 326 autologin = 0; 327 break; 328 329 case 'N': 330 if (strlcpy(netrc, optarg, sizeof(netrc)) 331 >= sizeof(netrc)) 332 errx(1, "%s: %s", optarg, 333 strerror(ENAMETOOLONG)); 334 break; 335 336 case 'o': 337 outfile = ftp_strdup(optarg); 338 if (strcmp(outfile, "-") == 0) 339 ttyout = stderr; 340 break; 341 342 case 'p': 343 passivemode = 1; 344 activefallback = 0; 345 break; 346 347 case 'P': 348 ftpport = optarg; 349 break; 350 351 case 'q': 352 quit_time = strtol(optarg, &ep, 10); 353 if (quit_time < 1 || *ep != '\0') 354 errx(1, "Bad quit value: %s", optarg); 355 break; 356 357 case 'r': 358 retry_connect = strtol(optarg, &ep, 10); 359 if (retry_connect < 1 || *ep != '\0') 360 errx(1, "Bad retry value: %s", optarg); 361 break; 362 363 case 'R': 364 restartautofetch = 1; 365 break; 366 367 case 's': 368 src_addr = optarg; 369 break; 370 371 case 't': 372 trace = 1; 373 break; 374 375 case 'T': 376 { 377 int targc; 378 char *targv[6], *oac; 379 char cmdbuf[MAX_C_NAME]; 380 381 /* look for `dir,max[,incr]' */ 382 targc = 0; 383 (void)strlcpy(cmdbuf, "-T", sizeof(cmdbuf)); 384 targv[targc++] = cmdbuf; 385 oac = ftp_strdup(optarg); 386 387 while ((cp = strsep(&oac, ",")) != NULL) { 388 if (*cp == '\0') { 389 warnx("Bad throttle value `%s'", 390 optarg); 391 return usage(); 392 } 393 targv[targc++] = cp; 394 if (targc >= 5) 395 break; 396 } 397 if (parserate(targc, targv, 1) == -1) { 398 return usage(); 399 } 400 free(oac); 401 break; 402 } 403 404 case 'u': 405 { 406 isupload = 1; 407 interactive = 0; 408 upload_path = ftp_strdup(optarg); 409 410 break; 411 } 412 413 case 'v': 414 progress = verbose = 1; 415 break; 416 417 case 'V': 418 progress = verbose = 0; 419 break; 420 421 case 'x': 422 sndbuf_size = strsuftoi(optarg); 423 if (sndbuf_size < 1) 424 errx(1, "Bad xferbuf value: %s", optarg); 425 rcvbuf_size = sndbuf_size; 426 break; 427 428 case '?': 429 if (optopt == '?') { 430 return usage_help(); 431 } 432 return usage(); 433 434 default: 435 errx(1, "unimplemented option -%c", ch); 436 } 437 } 438 /* set line buffering on ttyout */ 439 setvbuf(ttyout, NULL, _IOLBF, 0); 440 argc -= optind; 441 argv += optind; 442 443 cpend = 0; /* no pending replies */ 444 proxy = 0; /* proxy not active */ 445 crflag = 1; /* strip c.r. on ascii gets */ 446 sendport = -1; /* not using ports */ 447 448 if (src_addr != NULL) { 449 struct addrinfo hints; 450 int error; 451 452 memset(&hints, 0, sizeof(hints)); 453 hints.ai_family = family; 454 hints.ai_socktype = SOCK_STREAM; 455 hints.ai_flags = AI_PASSIVE; 456 error = getaddrinfo(src_addr, NULL, &hints, &bindai); 457 if (error) { 458 errx(1, "Can't lookup `%s': %s", src_addr, 459 (error == EAI_SYSTEM) ? strerror(errno) 460 : gai_strerror(error)); 461 } 462 } 463 464 /* 465 * Cache the user name and home directory. 466 */ 467 localhome = NULL; 468 localname = NULL; 469 anonuser = "anonymous"; 470 cp = getenv("HOME"); 471 if (! EMPTYSTRING(cp)) 472 localhome = ftp_strdup(cp); 473 pw = NULL; 474 cp = getlogin(); 475 if (cp != NULL) 476 pw = getpwnam(cp); 477 if (pw == NULL) 478 pw = getpwuid(getuid()); 479 if (pw != NULL) { 480 if (localhome == NULL && !EMPTYSTRING(pw->pw_dir)) 481 localhome = ftp_strdup(pw->pw_dir); 482 localname = ftp_strdup(pw->pw_name); 483 } 484 if (netrc[0] == '\0' && localhome != NULL) { 485 if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) || 486 strlcat(netrc, "/.netrc", sizeof(netrc)) >= sizeof(netrc)) { 487 warnx("%s/.netrc: %s", localhome, 488 strerror(ENAMETOOLONG)); 489 netrc[0] = '\0'; 490 } 491 } 492 if (localhome == NULL) 493 localhome = ftp_strdup("/"); 494 495 /* 496 * Every anonymous FTP server I've encountered will accept the 497 * string "username@", and will append the hostname itself. We 498 * do this by default since many servers are picky about not 499 * having a FQDN in the anonymous password. 500 * - thorpej@NetBSD.org 501 */ 502 len = strlen(anonuser) + 2; 503 anonpass = ftp_malloc(len); 504 (void)strlcpy(anonpass, anonuser, len); 505 (void)strlcat(anonpass, "@", len); 506 507 /* 508 * set all the defaults for options defined in 509 * struct option optiontab[] declared in cmdtab.c 510 */ 511 setupoption("anonpass", getenv("FTPANONPASS"), anonpass); 512 setupoption("ftp_proxy", getenv(FTP_PROXY), ""); 513 setupoption("http_proxy", getenv(HTTP_PROXY), ""); 514 setupoption("https_proxy", getenv(HTTPS_PROXY), ""); 515 setupoption("no_proxy", getenv(NO_PROXY), ""); 516 setupoption("pager", getenv("PAGER"), DEFAULTPAGER); 517 setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT); 518 setupoption("rprompt", getenv("FTPRPROMPT"), DEFAULTRPROMPT); 519 520 free(anonpass); 521 522 setttywidth(0); 523 #ifdef SIGINFO 524 (void)xsignal(SIGINFO, psummary); 525 #endif 526 (void)xsignal(SIGQUIT, psummary); 527 (void)xsignal(SIGUSR1, crankrate); 528 (void)xsignal(SIGUSR2, crankrate); 529 (void)xsignal(SIGWINCH, setttywidth); 530 531 if (argc > 0) { 532 if (isupload) { 533 rval = auto_put(argc, argv, upload_path); 534 sigint_or_rval_exit: 535 if (sigint_raised) { 536 (void)xsignal(SIGINT, SIG_DFL); 537 raise(SIGINT); 538 } 539 exit(rval); 540 } else if (strchr(argv[0], ':') != NULL 541 && ! isipv6addr(argv[0])) { 542 rval = auto_fetch(argc, argv); 543 if (rval >= 0) /* -1 == connected and cd-ed */ 544 goto sigint_or_rval_exit; 545 } else { 546 char *xargv[4], *uuser, *host; 547 char cmdbuf[MAXPATHLEN]; 548 549 if ((rval = sigsetjmp(toplevel, 1))) 550 goto sigint_or_rval_exit; 551 (void)xsignal(SIGINT, intr); 552 (void)xsignal(SIGPIPE, lostpeer); 553 uuser = NULL; 554 host = argv[0]; 555 cp = strchr(host, '@'); 556 if (cp) { 557 *cp = '\0'; 558 uuser = host; 559 host = cp + 1; 560 } 561 (void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf)); 562 xargv[0] = cmdbuf; 563 xargv[1] = host; 564 xargv[2] = argv[1]; 565 xargv[3] = NULL; 566 do { 567 int oautologin; 568 569 oautologin = autologin; 570 if (uuser != NULL) { 571 anonftp = 0; 572 autologin = 0; 573 } 574 setpeer(argc+1, xargv); 575 autologin = oautologin; 576 if (connected == 1 && uuser != NULL) 577 (void)ftp_login(host, uuser, NULL); 578 if (!retry_connect) 579 break; 580 if (!connected) { 581 macnum = 0; 582 fprintf(ttyout, 583 "Retrying in %d seconds...\n", 584 retry_connect); 585 sleep(retry_connect); 586 } 587 } while (!connected); 588 retry_connect = 0; /* connected, stop hiding msgs */ 589 } 590 } 591 if (isupload) { 592 return usage(); 593 } 594 595 #ifndef NO_EDITCOMPLETE 596 controlediting(); 597 #endif /* !NO_EDITCOMPLETE */ 598 599 (void)sigsetjmp(toplevel, 1); 600 (void)xsignal(SIGINT, intr); 601 (void)xsignal(SIGPIPE, lostpeer); 602 for (;;) 603 cmdscanner(); 604 } 605 606 /* 607 * Generate a prompt 608 */ 609 char * 610 prompt(void) 611 { 612 static char **promptopt; 613 static char buf[MAXPATHLEN]; 614 615 if (promptopt == NULL) { 616 struct option *o; 617 618 o = getoption("prompt"); 619 if (o == NULL) 620 errx(1, "prompt: no such option `prompt'"); 621 promptopt = &(o->value); 622 } 623 formatbuf(buf, sizeof(buf), *promptopt ? *promptopt : DEFAULTPROMPT); 624 return (buf); 625 } 626 627 /* 628 * Generate an rprompt 629 */ 630 char * 631 rprompt(void) 632 { 633 static char **rpromptopt; 634 static char buf[MAXPATHLEN]; 635 636 if (rpromptopt == NULL) { 637 struct option *o; 638 639 o = getoption("rprompt"); 640 if (o == NULL) 641 errx(1, "rprompt: no such option `rprompt'"); 642 rpromptopt = &(o->value); 643 } 644 formatbuf(buf, sizeof(buf), *rpromptopt ? *rpromptopt : DEFAULTRPROMPT); 645 return (buf); 646 } 647 648 /* 649 * Command parser. 650 */ 651 void 652 cmdscanner(void) 653 { 654 struct cmd *c; 655 char *p; 656 #ifndef NO_EDITCOMPLETE 657 int ch; 658 size_t num; 659 #endif 660 int len; 661 char cmdbuf[MAX_C_NAME]; 662 663 for (;;) { 664 #ifndef NO_EDITCOMPLETE 665 if (!editing) { 666 #endif /* !NO_EDITCOMPLETE */ 667 if (fromatty) { 668 fputs(prompt(), ttyout); 669 p = rprompt(); 670 if (*p) 671 fprintf(ttyout, "%s ", p); 672 } 673 (void)fflush(ttyout); 674 len = get_line(stdin, line, sizeof(line), NULL); 675 switch (len) { 676 case -1: /* EOF */ 677 case -2: /* error */ 678 if (fromatty) 679 putc('\n', ttyout); 680 justquit(); 681 /* NOTREACHED */ 682 case -3: /* too long; try again */ 683 fputs("Sorry, input line is too long.\n", 684 ttyout); 685 continue; 686 case 0: /* empty; try again */ 687 continue; 688 default: /* all ok */ 689 break; 690 } 691 #ifndef NO_EDITCOMPLETE 692 } else { 693 const char *buf; 694 HistEvent ev; 695 cursor_pos = NULL; 696 697 buf = el_gets(el, &ch); 698 num = ch; 699 if (buf == NULL || num == 0) { 700 if (fromatty) 701 putc('\n', ttyout); 702 justquit(); 703 } 704 if (num >= sizeof(line)) { 705 fputs("Sorry, input line is too long.\n", 706 ttyout); 707 break; 708 } 709 memcpy(line, buf, num); 710 if (line[--num] == '\n') { 711 line[num] = '\0'; 712 if (num == 0) 713 break; 714 } 715 history(hist, &ev, H_ENTER, buf); 716 } 717 #endif /* !NO_EDITCOMPLETE */ 718 719 makeargv(); 720 if (margc == 0) 721 continue; 722 c = getcmd(margv[0]); 723 if (c == (struct cmd *)-1) { 724 fputs("?Ambiguous command.\n", ttyout); 725 continue; 726 } 727 if (c == NULL) { 728 #if !defined(NO_EDITCOMPLETE) 729 /* 730 * attempt to el_parse() unknown commands. 731 * any command containing a ':' would be parsed 732 * as "[prog:]cmd ...", and will result in a 733 * false positive if prog != "ftp", so treat 734 * such commands as invalid. 735 */ 736 if (strchr(margv[0], ':') != NULL || 737 !editing || 738 el_parse(el, margc, (void *)margv) != 0) 739 #endif /* !NO_EDITCOMPLETE */ 740 fputs("?Invalid command.\n", ttyout); 741 continue; 742 } 743 if (c->c_conn && !connected) { 744 fputs("Not connected.\n", ttyout); 745 continue; 746 } 747 confirmrest = 0; 748 (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); 749 margv[0] = cmdbuf; 750 (*c->c_handler)(margc, margv); 751 if (bell && c->c_bell) 752 (void)putc('\007', ttyout); 753 if (c->c_handler != help) 754 break; 755 } 756 (void)xsignal(SIGINT, intr); 757 (void)xsignal(SIGPIPE, lostpeer); 758 } 759 760 struct cmd * 761 getcmd(const char *name) 762 { 763 const char *p, *q; 764 struct cmd *c, *found; 765 int nmatches, longest; 766 767 if (name == NULL) 768 return (0); 769 770 longest = 0; 771 nmatches = 0; 772 found = 0; 773 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 774 for (q = name; *q == *p++; q++) 775 if (*q == 0) /* exact match? */ 776 return (c); 777 if (!*q) { /* the name was a prefix */ 778 if (q - name > longest) { 779 longest = q - name; 780 nmatches = 1; 781 found = c; 782 } else if (q - name == longest) 783 nmatches++; 784 } 785 } 786 if (nmatches > 1) 787 return ((struct cmd *)-1); 788 return (found); 789 } 790 791 /* 792 * Slice a string up into argc/argv. 793 */ 794 795 int slrflag; 796 797 void 798 makeargv(void) 799 { 800 char *argp; 801 802 stringbase = line; /* scan from first of buffer */ 803 argbase = argbuf; /* store from first of buffer */ 804 slrflag = 0; 805 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 806 for (margc = 0; ; margc++) { 807 argp = slurpstring(); 808 ftp_sl_add(marg_sl, argp); 809 if (argp == NULL) 810 break; 811 } 812 #ifndef NO_EDITCOMPLETE 813 if (cursor_pos == line) { 814 cursor_argc = 0; 815 cursor_argo = 0; 816 } else if (cursor_pos != NULL) { 817 cursor_argc = margc; 818 cursor_argo = strlen(margv[margc-1]); 819 } 820 #endif /* !NO_EDITCOMPLETE */ 821 } 822 823 #ifdef NO_EDITCOMPLETE 824 #define INC_CHKCURSOR(x) (x)++ 825 #else /* !NO_EDITCOMPLETE */ 826 #define INC_CHKCURSOR(x) { (x)++ ; \ 827 if (x == cursor_pos) { \ 828 cursor_argc = margc; \ 829 cursor_argo = ap-argbase; \ 830 cursor_pos = NULL; \ 831 } } 832 833 #endif /* !NO_EDITCOMPLETE */ 834 835 /* 836 * Parse string into argbuf; 837 * implemented with FSM to 838 * handle quoting and strings 839 */ 840 char * 841 slurpstring(void) 842 { 843 static char bangstr[2] = { '!', '\0' }; 844 static char dollarstr[2] = { '$', '\0' }; 845 int got_one = 0; 846 char *sb = stringbase; 847 char *ap = argbase; 848 char *tmp = argbase; /* will return this if token found */ 849 850 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 851 switch (slrflag) { /* and $ as token for macro invoke */ 852 case 0: 853 slrflag++; 854 INC_CHKCURSOR(stringbase); 855 return ((*sb == '!') ? bangstr : dollarstr); 856 case 1: 857 slrflag++; 858 altarg = stringbase; 859 break; 860 default: 861 break; 862 } 863 } 864 865 S0: 866 switch (*sb) { 867 868 case '\0': 869 goto OUT; 870 871 case ' ': 872 case '\t': 873 INC_CHKCURSOR(sb); 874 goto S0; 875 876 default: 877 switch (slrflag) { 878 case 0: 879 slrflag++; 880 break; 881 case 1: 882 slrflag++; 883 altarg = sb; 884 break; 885 default: 886 break; 887 } 888 goto S1; 889 } 890 891 S1: 892 switch (*sb) { 893 894 case ' ': 895 case '\t': 896 case '\0': 897 goto OUT; /* end of token */ 898 899 case '\\': 900 INC_CHKCURSOR(sb); 901 goto S2; /* slurp next character */ 902 903 case '"': 904 INC_CHKCURSOR(sb); 905 goto S3; /* slurp quoted string */ 906 907 default: 908 *ap = *sb; /* add character to token */ 909 ap++; 910 INC_CHKCURSOR(sb); 911 got_one = 1; 912 goto S1; 913 } 914 915 S2: 916 switch (*sb) { 917 918 case '\0': 919 goto OUT; 920 921 default: 922 *ap = *sb; 923 ap++; 924 INC_CHKCURSOR(sb); 925 got_one = 1; 926 goto S1; 927 } 928 929 S3: 930 switch (*sb) { 931 932 case '\0': 933 goto OUT; 934 935 case '"': 936 INC_CHKCURSOR(sb); 937 goto S1; 938 939 default: 940 *ap = *sb; 941 ap++; 942 INC_CHKCURSOR(sb); 943 got_one = 1; 944 goto S3; 945 } 946 947 OUT: 948 if (got_one) 949 *ap++ = '\0'; 950 argbase = ap; /* update storage pointer */ 951 stringbase = sb; /* update scan pointer */ 952 if (got_one) { 953 return (tmp); 954 } 955 switch (slrflag) { 956 case 0: 957 slrflag++; 958 break; 959 case 1: 960 slrflag++; 961 altarg = NULL; 962 break; 963 default: 964 break; 965 } 966 return (NULL); 967 } 968 969 /* 970 * Help/usage command. 971 * Call each command handler with argc == 0 and argv[0] == name. 972 */ 973 void 974 help(int argc, char *argv[]) 975 { 976 struct cmd *c; 977 char *nargv[1], *cmd; 978 const char *p; 979 int isusage; 980 981 cmd = argv[0]; 982 isusage = (strcmp(cmd, "usage") == 0); 983 if (argc == 0 || (isusage && argc == 1)) { 984 UPRINTF("usage: %s [command ...]\n", cmd); 985 return; 986 } 987 if (argc == 1) { 988 StringList *buf; 989 990 buf = ftp_sl_init(); 991 fprintf(ttyout, 992 "%sommands may be abbreviated. Commands are:\n\n", 993 proxy ? "Proxy c" : "C"); 994 for (c = cmdtab; (p = c->c_name) != NULL; c++) 995 if (!proxy || c->c_proxy) 996 ftp_sl_add(buf, ftp_strdup(p)); 997 list_vertical(buf); 998 sl_free(buf, 1); 999 return; 1000 } 1001 1002 #define HELPINDENT ((int) sizeof("disconnect")) 1003 1004 while (--argc > 0) { 1005 char *arg; 1006 char cmdbuf[MAX_C_NAME]; 1007 1008 arg = *++argv; 1009 c = getcmd(arg); 1010 if (c == (struct cmd *)-1) 1011 fprintf(ttyout, "?Ambiguous %s command `%s'\n", 1012 cmd, arg); 1013 else if (c == NULL) 1014 fprintf(ttyout, "?Invalid %s command `%s'\n", 1015 cmd, arg); 1016 else { 1017 if (isusage) { 1018 (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); 1019 nargv[0] = cmdbuf; 1020 (*c->c_handler)(0, nargv); 1021 } else 1022 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, 1023 c->c_name, c->c_help); 1024 } 1025 } 1026 } 1027 1028 struct option * 1029 getoption(const char *name) 1030 { 1031 const char *p; 1032 struct option *c; 1033 1034 if (name == NULL) 1035 return (NULL); 1036 for (c = optiontab; (p = c->name) != NULL; c++) { 1037 if (strcasecmp(p, name) == 0) 1038 return (c); 1039 } 1040 return (NULL); 1041 } 1042 1043 char * 1044 getoptionvalue(const char *name) 1045 { 1046 struct option *c; 1047 1048 if (name == NULL) 1049 errx(1, "getoptionvalue: invoked with NULL name"); 1050 c = getoption(name); 1051 if (c != NULL) 1052 return (c->value); 1053 errx(1, "getoptionvalue: invoked with unknown option `%s'", name); 1054 /* NOTREACHED */ 1055 } 1056 1057 static void 1058 setupoption(const char *name, const char *value, const char *defaultvalue) 1059 { 1060 set_option(name, value ? value : defaultvalue, 0); 1061 } 1062 1063 static void 1064 synopsis(FILE * stream) 1065 { 1066 const char * progname = getprogname(); 1067 1068 fprintf(stream, 1069 "usage: %s [-46AadefginpRtVv] [-N NETRC] [-o OUTPUT] [-P PORT] [-q QUITTIME]\n" 1070 " [-r RETRY] [-s SRCADDR] [-T DIR,MAX[,INC]] [-x XFERSIZE]\n" 1071 " [[USER@]HOST [PORT]]\n" 1072 " [[USER@]HOST:[PATH][/]]\n" 1073 " [file:///PATH]\n" 1074 " [ftp://[USER[:PASSWORD]@]HOST[:PORT]/PATH[/][;type=TYPE]]\n" 1075 " [http://[USER[:PASSWORD]@]HOST[:PORT]/PATH]\n" 1076 #ifdef WITH_SSL 1077 " [https://[USER[:PASSWORD]@]HOST[:PORT]/PATH]\n" 1078 #endif 1079 " ...\n" 1080 " %s -u URL FILE ...\n" 1081 " %s -?\n", 1082 progname, progname, progname); 1083 } 1084 1085 static int 1086 usage_help(void) 1087 { 1088 synopsis(stdout); 1089 #ifndef NO_USAGE 1090 printf( 1091 " -4 Only use IPv4 addresses\n" 1092 " -6 Only use IPv6 addresses\n" 1093 " -A Force active mode\n" 1094 " -a Use anonymous login\n" 1095 " -d Enable debugging\n" 1096 " -e Disable command-line editing\n" 1097 " -f Force cache reload for FTP or HTTP proxy transfers\n" 1098 " -g Disable file name globbing\n" 1099 " -i Disable interactive prompt during multiple file transfers\n" 1100 " -N NETRC Use NETRC instead of ~/.netrc\n" 1101 " -n Disable auto-login\n" 1102 " -o OUTPUT Save auto-fetched files to OUTPUT\n" 1103 " -P PORT Use port PORT\n" 1104 " -p Force passive mode\n" 1105 " -q QUITTIME Quit if connection stalls for QUITTIME seconds\n" 1106 " -R Restart non-proxy auto-fetch\n" 1107 " -r RETRY Retry failed connection attempts after RETRY seconds\n" 1108 " -s SRCADDR Use source address SRCADDR\n" 1109 " -t Enable packet tracing\n" 1110 " -T DIR,MAX[,INC]\n" 1111 " Set maximum transfer rate for direction DIR to MAX bytes/s,\n" 1112 " with optional increment INC bytes/s\n" 1113 " -u URL URL to upload file arguments to\n" 1114 " -V Disable verbose and progress\n" 1115 " -v Enable verbose and progress\n" 1116 " -x XFERSIZE Set socket send and receive size to XFERSIZE\n" 1117 " -? Display this help and exit\n" 1118 ); 1119 #endif 1120 return EXIT_SUCCESS; 1121 } 1122 1123 static int 1124 usage(void) 1125 { 1126 synopsis(stderr); 1127 return EXIT_FAILURE; 1128 } 1129