1 /* $OpenBSD: main.c,v 1.10 1996/12/18 01:59:15 michaels 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 #ifndef lint 37 static char copyright[] = 38 "@(#) 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 static char rcsid[] = "$OpenBSD: main.c,v 1.10 1996/12/18 01:59:15 michaels Exp $"; 47 #endif 48 #endif /* not lint */ 49 50 /* 51 * FTP User Program -- Command Interface. 52 */ 53 /*#include <sys/ioctl.h>*/ 54 #include <sys/types.h> 55 #include <sys/file.h> 56 #include <sys/socket.h> 57 #include <netinet/in.h> 58 59 #include <arpa/ftp.h> 60 61 #include <ctype.h> 62 #include <err.h> 63 #include <netdb.h> 64 #include <pwd.h> 65 #include <signal.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 71 #include "ftp_var.h" 72 73 #define HASHBYTES 1024 /* default buffer size for drawing hash marks */ 74 75 int 76 main(argc, argv) 77 int argc; 78 char *argv[]; 79 { 80 int ch, top; 81 struct passwd *pw = NULL; 82 char *cp, homedir[MAXPATHLEN]; 83 int force_port = 0; 84 85 sp = getservbyname("ftp", "tcp"); 86 if (sp == 0) 87 errx(1, "ftp/tcp: unknown service"); 88 doglob = 1; 89 interactive = 1; 90 autologin = 1; 91 mark = HASHBYTES; 92 93 while ((ch = getopt(argc, argv, "p:r:dgintv")) != EOF) { 94 switch (ch) { 95 case 'd': 96 options |= SO_DEBUG; 97 debug++; 98 break; 99 100 case 'g': 101 doglob = 0; 102 break; 103 104 case 'i': 105 interactive = 0; 106 break; 107 108 case 'n': 109 autologin = 0; 110 break; 111 112 case 'p': 113 force_port = atoi(optarg); 114 break; 115 116 case 'r': 117 if (isdigit(*optarg)) 118 retry_connect = atoi(optarg); 119 else { 120 extern char *__progname; 121 (void)fprintf(stderr, 122 "%s: -r requires numeric argument\n", 123 __progname); 124 exit(1); 125 } 126 break; 127 128 case 't': 129 trace++; 130 break; 131 132 case 'v': 133 verbose++; 134 break; 135 136 default: 137 (void)fprintf(stderr, "usage: " 138 "ftp [-dgintv] [-r<seconds>] [host [port]]\n"); 139 exit(1); 140 } 141 } 142 argc -= optind; 143 argv += optind; 144 145 fromatty = isatty(fileno(stdin)); 146 if (fromatty) 147 verbose++; 148 cpend = 0; /* no pending replies */ 149 proxy = 0; /* proxy not active */ 150 passivemode = 0; /* passive mode not active */ 151 crflag = 1; /* strip c.r. on ascii gets */ 152 sendport = -1; /* not using ports */ 153 /* 154 * Set up the home directory in case we're globbing. 155 */ 156 cp = getlogin(); 157 if (cp != NULL) { 158 pw = getpwnam(cp); 159 } 160 if (pw == NULL) 161 pw = getpwuid(getuid()); 162 if (pw != NULL) { 163 home = homedir; 164 (void) strcpy(home, pw->pw_dir); 165 } 166 167 if (argc > 0 && strchr(argv[0], ':')) { 168 int ret = 0; 169 anonftp = 1; 170 171 while (argc > 0 && strchr(argv[0], ':')) { 172 char *xargv[5]; 173 extern char *__progname; 174 char portstr[20], *p, *bufp = NULL; 175 char *host = NULL, *dir = NULL, *file = NULL; 176 int xargc = 2, looping = 0, tmp; 177 178 if (setjmp(toplevel)) 179 exit(0); 180 (void) signal(SIGINT, intr); 181 (void) signal(SIGPIPE, lostpeer); 182 xargv[0] = __progname; 183 184 host = strdup(argv[0]); 185 if (host == NULL) { 186 ret = 1; 187 goto bail; 188 } 189 if (!strncmp(host, "http://", strlen("http://"))) { 190 http_fetch(host); 191 goto bail; 192 } 193 if (strncmp(host, "ftp://", strlen("ftp://")) == 0) { 194 host += strlen("ftp://"); 195 p = strchr(host, '/'); 196 } 197 else 198 p = strchr(host, ':'); 199 *p = '\0'; 200 201 xargv[1] = host; 202 xargc = 2; 203 if (force_port) { 204 xargv[xargc++] = portstr; 205 snprintf(portstr, sizeof portstr, "%d", 206 force_port); 207 } 208 xargv[xargc] = NULL; 209 setpeer(xargc, xargv); 210 if (!connected) { 211 printf("failed to connect to %s\n", host); 212 ret = 1; 213 goto bail; 214 } 215 *argv = strchr(argv[0], ':') + 1; 216 do { 217 dir = *argv; 218 p = strrchr(dir, '/'); 219 if (p != NULL) { 220 *p = '\0'; 221 file = ++p; 222 } else { 223 file = dir; 224 dir = NULL; 225 } 226 if (dir != NULL && *dir != '\0') { 227 xargv[1] = dir; 228 xargv[2] = NULL; 229 xargc = 2; 230 cd(xargc, xargv); 231 } 232 xargv[1] = *file == '\0' ? "/" : file; 233 xargv[2] = NULL; 234 xargc = 2; 235 tmp = verbose; 236 verbose = -1; 237 if (cd(xargc, xargv) == 0) { 238 verbose = tmp; 239 goto CLINE_CD; 240 } 241 verbose = tmp; 242 if (!looping) { 243 setbinary(NULL, 0); 244 looping = 1; 245 } 246 /* fetch file */ 247 xargv[1] = file; 248 xargv[2] = NULL; 249 xargc = 2; 250 get(xargc, xargv); 251 --argc; 252 argv++; 253 } while (argc > 0 && strchr(argv[0], ':') == NULL); 254 255 /* get ready for the next file */ 256 bail: 257 if (bufp) { 258 free(bufp); 259 bufp = NULL; 260 } 261 if (connected) 262 disconnect(1, xargv); 263 } 264 exit(ret); 265 } 266 if (argc > 0) { 267 char *xargv[5]; 268 extern char *__progname; 269 270 if (setjmp(toplevel)) 271 exit(0); 272 (void) signal(SIGINT, intr); 273 (void) signal(SIGPIPE, lostpeer); 274 xargv[0] = __progname; 275 xargv[1] = argv[0]; 276 xargv[2] = argv[1]; 277 xargv[3] = argv[2]; 278 xargv[4] = NULL; 279 do { 280 setpeer(argc+1, xargv); 281 if (!retry_connect) 282 break; 283 if (!connected) { 284 macnum = 0; 285 printf("Retrying...\n"); 286 sleep(retry_connect); 287 } 288 } while (!connected); 289 } 290 CLINE_CD: 291 top = setjmp(toplevel) == 0; 292 if (top) { 293 (void) signal(SIGINT, intr); 294 (void) signal(SIGPIPE, lostpeer); 295 } 296 for (;;) { 297 cmdscanner(top); 298 top = 1; 299 } 300 } 301 302 void 303 intr() 304 { 305 306 longjmp(toplevel, 1); 307 } 308 309 void 310 lostpeer() 311 { 312 313 if (connected) { 314 if (cout != NULL) { 315 (void) shutdown(fileno(cout), 1+1); 316 (void) fclose(cout); 317 cout = NULL; 318 } 319 if (data >= 0) { 320 (void) shutdown(data, 1+1); 321 (void) close(data); 322 data = -1; 323 } 324 connected = 0; 325 } 326 pswitch(1); 327 if (connected) { 328 if (cout != NULL) { 329 (void) shutdown(fileno(cout), 1+1); 330 (void) fclose(cout); 331 cout = NULL; 332 } 333 connected = 0; 334 } 335 proxflag = 0; 336 pswitch(0); 337 } 338 339 /* 340 char * 341 tail(filename) 342 char *filename; 343 { 344 char *s; 345 346 while (*filename) { 347 s = strrchr(filename, '/'); 348 if (s == NULL) 349 break; 350 if (s[1]) 351 return (s + 1); 352 *s = '\0'; 353 } 354 return (filename); 355 } 356 */ 357 358 /* 359 * Command parser. 360 */ 361 void 362 cmdscanner(top) 363 int top; 364 { 365 struct cmd *c; 366 int l; 367 368 if (!top) 369 (void) putchar('\n'); 370 for (;;) { 371 if (fromatty) { 372 printf("ftp> "); 373 (void) fflush(stdout); 374 } 375 if (fgets(line, sizeof line, stdin) == NULL) 376 quit(0, 0); 377 l = strlen(line); 378 if (l == 0) 379 break; 380 if (line[--l] == '\n') { 381 if (l == 0) 382 break; 383 line[l] = '\0'; 384 } else if (l == sizeof(line) - 2) { 385 printf("sorry, input line too long\n"); 386 while ((l = getchar()) != '\n' && l != EOF) 387 /* void */; 388 break; 389 } /* else it was a line without a newline */ 390 makeargv(); 391 if (margc == 0) { 392 continue; 393 } 394 c = getcmd(margv[0]); 395 if (c == (struct cmd *)-1) { 396 printf("?Ambiguous command\n"); 397 continue; 398 } 399 if (c == 0) { 400 printf("?Invalid command\n"); 401 continue; 402 } 403 if (c->c_conn && !connected) { 404 printf("Not connected.\n"); 405 continue; 406 } 407 (*c->c_handler)(margc, margv); 408 if (bell && c->c_bell) 409 (void) putchar('\007'); 410 if (c->c_handler != help) 411 break; 412 } 413 (void) signal(SIGINT, intr); 414 (void) signal(SIGPIPE, lostpeer); 415 } 416 417 struct cmd * 418 getcmd(name) 419 char *name; 420 { 421 char *p, *q; 422 struct cmd *c, *found; 423 int nmatches, longest; 424 425 if (name == NULL) 426 return (0); 427 428 longest = 0; 429 nmatches = 0; 430 found = 0; 431 for (c = cmdtab; p = c->c_name; c++) { 432 for (q = name; *q == *p++; q++) 433 if (*q == 0) /* exact match? */ 434 return (c); 435 if (!*q) { /* the name was a prefix */ 436 if (q - name > longest) { 437 longest = q - name; 438 nmatches = 1; 439 found = c; 440 } else if (q - name == longest) 441 nmatches++; 442 } 443 } 444 if (nmatches > 1) 445 return ((struct cmd *)-1); 446 return (found); 447 } 448 449 /* 450 * Slice a string up into argc/argv. 451 */ 452 453 int slrflag; 454 455 void 456 makeargv() 457 { 458 char **argp; 459 460 argp = margv; 461 stringbase = line; /* scan from first of buffer */ 462 argbase = argbuf; /* store from first of buffer */ 463 slrflag = 0; 464 for (margc = 0; ; margc++) { 465 /* Expand array if necessary */ 466 if (margc == margvlen) { 467 margv = (margvlen == 0) 468 ? (char **)malloc(20 * sizeof(char *)) 469 : (char **)realloc(margv, 470 (margvlen + 20)*sizeof(char *)); 471 if (margv == NULL) 472 errx(1, "cannot realloc argv array"); 473 margvlen += 20; 474 argp = margv + margc; 475 } 476 477 if ((*argp++ = slurpstring()) == NULL) 478 break; 479 } 480 481 } 482 483 /* 484 * Parse string into argbuf; 485 * implemented with FSM to 486 * handle quoting and strings 487 */ 488 char * 489 slurpstring() 490 { 491 int got_one = 0; 492 char *sb = stringbase; 493 char *ap = argbase; 494 char *tmp = argbase; /* will return this if token found */ 495 496 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 497 switch (slrflag) { /* and $ as token for macro invoke */ 498 case 0: 499 slrflag++; 500 stringbase++; 501 return ((*sb == '!') ? "!" : "$"); 502 /* NOTREACHED */ 503 case 1: 504 slrflag++; 505 altarg = stringbase; 506 break; 507 default: 508 break; 509 } 510 } 511 512 S0: 513 switch (*sb) { 514 515 case '\0': 516 goto OUT; 517 518 case ' ': 519 case '\t': 520 sb++; goto S0; 521 522 default: 523 switch (slrflag) { 524 case 0: 525 slrflag++; 526 break; 527 case 1: 528 slrflag++; 529 altarg = sb; 530 break; 531 default: 532 break; 533 } 534 goto S1; 535 } 536 537 S1: 538 switch (*sb) { 539 540 case ' ': 541 case '\t': 542 case '\0': 543 goto OUT; /* end of token */ 544 545 case '\\': 546 sb++; goto S2; /* slurp next character */ 547 548 case '"': 549 sb++; goto S3; /* slurp quoted string */ 550 551 default: 552 *ap++ = *sb++; /* add character to token */ 553 got_one = 1; 554 goto S1; 555 } 556 557 S2: 558 switch (*sb) { 559 560 case '\0': 561 goto OUT; 562 563 default: 564 *ap++ = *sb++; 565 got_one = 1; 566 goto S1; 567 } 568 569 S3: 570 switch (*sb) { 571 572 case '\0': 573 goto OUT; 574 575 case '"': 576 sb++; goto S1; 577 578 default: 579 *ap++ = *sb++; 580 got_one = 1; 581 goto S3; 582 } 583 584 OUT: 585 if (got_one) 586 *ap++ = '\0'; 587 argbase = ap; /* update storage pointer */ 588 stringbase = sb; /* update scan pointer */ 589 if (got_one) { 590 return (tmp); 591 } 592 switch (slrflag) { 593 case 0: 594 slrflag++; 595 break; 596 case 1: 597 slrflag++; 598 altarg = (char *) 0; 599 break; 600 default: 601 break; 602 } 603 return ((char *)0); 604 } 605 606 #define HELPINDENT ((int) sizeof ("directory")) 607 608 /* 609 * Help command. 610 * Call each command handler with argc == 0 and argv[0] == name. 611 */ 612 void 613 help(argc, argv) 614 int argc; 615 char *argv[]; 616 { 617 struct cmd *c; 618 619 if (argc == 1) { 620 int i, j, w, k; 621 int columns, width = 0, lines; 622 623 printf("Commands may be abbreviated. Commands are:\n\n"); 624 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 625 int len = strlen(c->c_name); 626 627 if (len > width) 628 width = len; 629 } 630 width = (width + 8) &~ 7; 631 columns = 80 / width; 632 if (columns == 0) 633 columns = 1; 634 lines = (NCMDS + columns - 1) / columns; 635 for (i = 0; i < lines; i++) { 636 for (j = 0; j < columns; j++) { 637 c = cmdtab + j * lines + i; 638 if (c->c_name && (!proxy || c->c_proxy)) { 639 printf("%s", c->c_name); 640 } 641 else if (c->c_name) { 642 for (k=0; k < strlen(c->c_name); k++) { 643 (void) putchar(' '); 644 } 645 } 646 if (c + lines >= &cmdtab[NCMDS]) { 647 printf("\n"); 648 break; 649 } 650 w = strlen(c->c_name); 651 while (w < width) { 652 w = (w + 8) &~ 7; 653 (void) putchar('\t'); 654 } 655 } 656 } 657 return; 658 } 659 while (--argc > 0) { 660 char *arg; 661 arg = *++argv; 662 c = getcmd(arg); 663 if (c == (struct cmd *)-1) 664 printf("?Ambiguous help command %s\n", arg); 665 else if (c == (struct cmd *)0) 666 printf("?Invalid help command %s\n", arg); 667 else 668 printf("%-*s\t%s\n", HELPINDENT, 669 c->c_name, c->c_help); 670 } 671 } 672