1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * FTP User Program -- Command Interface. 44 */ 45 #define EXTERN 46 #include "ftp_var.h" 47 #include <deflt.h> /* macros that make using libcmd easier */ 48 49 static void usage(void); 50 static void timeout_sig(int sig); 51 static void cmdscanner(int top); 52 static void intr(int sig); 53 static char *slurpstring(void); 54 extern int use_eprt; 55 56 boolean_t ls_invokes_NLST = B_TRUE; 57 58 #include <gssapi/gssapi.h> 59 #include <gssapi/gssapi_ext.h> 60 #define GETOPT_STR "dginptvET:axfm:" 61 #define USAGE_STR "[-adfginptvx] [-m mech] [-T timeout] " \ 62 "[hostname [port]]" 63 64 int 65 main(int argc, char *argv[]) 66 { 67 char *cp; 68 int c, top; 69 struct passwd *pw = NULL; 70 char homedir[MAXPATHLEN]; 71 char *temp_string = NULL; 72 73 (void) setlocale(LC_ALL, ""); 74 75 buf = (char *)memalign(getpagesize(), FTPBUFSIZ); 76 if (buf == NULL) { 77 (void) fprintf(stderr, "ftp: memory allocation failed\n"); 78 return (1); 79 } 80 81 timeoutms = timeout = 0; 82 doglob = 1; 83 interactive = 1; 84 autologin = 1; 85 86 autoauth = 0; 87 fflag = 0; 88 autoencrypt = 0; 89 goteof = 0; 90 mechstr[0] = '\0'; 91 92 sendport = -1; /* tri-state variable. start out in "automatic" mode. */ 93 passivemode = 0; 94 95 while ((c = getopt(argc, argv, GETOPT_STR)) != EOF) { 96 switch (c) { 97 case 'd': 98 options |= SO_DEBUG; 99 debug++; 100 break; 101 102 case 'g': 103 doglob = 0; 104 break; 105 106 case 'i': 107 interactive = 0; 108 break; 109 110 case 'n': 111 autologin = 0; 112 break; 113 114 case 'p': 115 passivemode = 1; 116 break; 117 118 case 't': 119 trace++; 120 break; 121 122 case 'v': 123 verbose++; 124 break; 125 126 /* undocumented option: allows testing of EPRT */ 127 case 'E': 128 use_eprt = 1; 129 break; 130 131 case 'T': 132 if (!isdigit(*optarg)) { 133 (void) fprintf(stderr, 134 "ftp: bad timeout: \"%s\"\n", optarg); 135 break; 136 } 137 timeout = atoi(optarg); 138 timeoutms = timeout * MILLISEC; 139 break; 140 141 case 'a': 142 autoauth = 1; 143 break; 144 145 case 'f': 146 autoauth = 1; 147 fflag = 1; 148 break; 149 150 case 'm': 151 autoauth = 1; 152 call(setmech, "ftp", optarg, 0); 153 if (code != 0) 154 exit(1); 155 break; 156 157 case 'x': 158 autoauth = 1; 159 autoencrypt = 1; 160 break; 161 162 case '?': 163 default: 164 usage(); 165 } 166 } 167 argc -= optind; 168 argv += optind; 169 170 if (argc > 2) 171 usage(); 172 173 fromatty = isatty(fileno(stdin)); 174 /* 175 * Scan env, then DEFAULTFTPFILE 176 * for FTP_LS_SENDS_NLST 177 */ 178 temp_string = getenv("FTP_LS_SENDS_NLST"); 179 if (temp_string == NULL) { /* env var not set */ 180 if (defopen(DEFAULTFTPFILE) == 0) { 181 /* 182 * turn off case sensitivity 183 */ 184 int flags = defcntl(DC_GETFLAGS, 0); 185 186 TURNOFF(flags, DC_CASE); 187 (void) defcntl(DC_SETFLAGS, flags); 188 189 temp_string = defread("FTP_LS_SENDS_NLST="); 190 (void) defopen(NULL); /* close default file */ 191 } 192 } 193 if (temp_string != NULL && 194 strncasecmp(temp_string, "n", 1) == 0) 195 ls_invokes_NLST = B_FALSE; 196 197 /* 198 * Set up defaults for FTP. 199 */ 200 (void) strcpy(typename, "ascii"), type = TYPE_A; 201 (void) strcpy(formname, "non-print"), form = FORM_N; 202 (void) strcpy(modename, "stream"), mode = MODE_S; 203 (void) strcpy(structname, "file"), stru = STRU_F; 204 (void) strcpy(bytename, "8"), bytesize = 8; 205 if (fromatty) 206 verbose++; 207 cpend = 0; /* no pending replies */ 208 proxy = 0; /* proxy not active */ 209 crflag = 1; /* strip c.r. on ascii gets */ 210 211 if (mechstr[0] == '\0') { 212 strlcpy(mechstr, FTP_DEF_MECH, MECH_SZ); 213 } 214 215 /* 216 * Set up the home directory in case we're globbing. 217 */ 218 cp = getlogin(); 219 if (cp != NULL) { 220 pw = getpwnam(cp); 221 } 222 if (pw == NULL) 223 pw = getpwuid(getuid()); 224 if (pw != NULL) { 225 home = homedir; 226 (void) strcpy(home, pw->pw_dir); 227 } 228 if (setjmp(timeralarm)) { 229 (void) fflush(stdout); 230 (void) printf("Connection timeout\n"); 231 exit(1); 232 } 233 (void) signal(SIGALRM, timeout_sig); 234 reset_timer(); 235 if (argc > 0) { 236 int nargc = 0; 237 char *nargv[4]; 238 239 if (setjmp(toplevel)) 240 return (0); 241 (void) signal(SIGINT, intr); 242 (void) signal(SIGPIPE, lostpeer); 243 nargv[nargc++] = "ftp"; 244 nargv[nargc++] = argv[0]; /* hostname */ 245 if (argc > 1) 246 nargv[nargc++] = argv[1]; /* port */ 247 nargv[nargc] = NULL; 248 setpeer(nargc, nargv); 249 } 250 top = setjmp(toplevel) == 0; 251 if (top) { 252 (void) signal(SIGINT, intr); 253 (void) signal(SIGPIPE, lostpeer); 254 } 255 256 for (;;) { 257 cmdscanner(top); 258 top = 1; 259 } 260 } 261 262 static void 263 usage(void) 264 { 265 (void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR); 266 exit(1); 267 } 268 269 void 270 reset_timer() 271 { 272 /* The test is just to reduce syscalls if timeouts aren't used */ 273 if (timeout) 274 alarm(timeout); 275 } 276 277 void 278 stop_timer() 279 { 280 if (timeout) 281 alarm(0); 282 } 283 284 /*ARGSUSED*/ 285 static void 286 timeout_sig(int sig) 287 { 288 longjmp(timeralarm, 1); 289 } 290 291 /*ARGSUSED*/ 292 static void 293 intr(int sig) 294 { 295 longjmp(toplevel, 1); 296 } 297 298 /*ARGSUSED*/ 299 void 300 lostpeer(int sig) 301 { 302 extern FILE *ctrl_out; 303 extern int data; 304 305 if (connected) { 306 if (ctrl_out != NULL) { 307 (void) shutdown(fileno(ctrl_out), 1+1); 308 (void) fclose(ctrl_out); 309 ctrl_out = NULL; 310 } 311 if (data >= 0) { 312 (void) shutdown(data, 1+1); 313 (void) close(data); 314 data = -1; 315 } 316 connected = 0; 317 318 auth_type = AUTHTYPE_NONE; 319 clevel = dlevel = PROT_C; 320 goteof = 0; 321 } 322 pswitch(1); 323 if (connected) { 324 if (ctrl_out != NULL) { 325 (void) shutdown(fileno(ctrl_out), 1+1); 326 (void) fclose(ctrl_out); 327 ctrl_out = NULL; 328 } 329 connected = 0; 330 331 auth_type = AUTHTYPE_NONE; 332 clevel = dlevel = PROT_C; 333 goteof = 0; 334 } 335 proxflag = 0; 336 pswitch(0); 337 } 338 339 /* 340 * Command parser. 341 */ 342 static void 343 cmdscanner(int top) 344 { 345 struct cmd *c; 346 347 if (!top) 348 (void) putchar('\n'); 349 for (;;) { 350 stop_timer(); 351 if (fromatty) { 352 (void) printf("ftp> "); 353 (void) fflush(stdout); 354 } 355 if (fgets(line, sizeof (line), stdin) == 0) { 356 if (feof(stdin) || ferror(stdin)) 357 quit(0, NULL); 358 break; 359 } 360 if (line[0] == 0) 361 break; 362 /* If not all, just discard rest of line */ 363 if (line[strlen(line)-1] != '\n') { 364 while (fgetc(stdin) != '\n' && !feof(stdin) && 365 !ferror(stdin)) 366 ; 367 (void) printf("Line too long\n"); 368 continue; 369 } else 370 line[strlen(line)-1] = 0; 371 372 makeargv(); 373 if (margc == 0) { 374 continue; 375 } 376 c = getcmd(margv[0]); 377 if (c == (struct cmd *)-1) { 378 (void) printf("?Ambiguous command\n"); 379 continue; 380 } 381 if (c == 0) { 382 (void) printf("?Invalid command\n"); 383 continue; 384 } 385 if (c->c_conn && !connected) { 386 (void) printf("Not connected.\n"); 387 continue; 388 } 389 reset_timer(); 390 (*c->c_handler)(margc, margv); 391 #ifndef CTRL 392 #define CTRL(c) ((c)&037) 393 #endif 394 stop_timer(); 395 if (bell && c->c_bell) 396 (void) putchar(CTRL('g')); 397 if (c->c_handler != help) 398 break; 399 } 400 (void) signal(SIGINT, intr); 401 (void) signal(SIGPIPE, lostpeer); 402 } 403 404 struct cmd * 405 getcmd(char *name) 406 { 407 char *p, *q; 408 struct cmd *c, *found; 409 int nmatches, longest; 410 extern struct cmd cmdtab[]; 411 412 if (name == NULL) 413 return (0); 414 415 longest = 0; 416 nmatches = 0; 417 found = 0; 418 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 419 for (q = name; *q == *p++; q++) 420 if (*q == 0) /* exact match? */ 421 return (c); 422 if (!*q) { /* the name was a prefix */ 423 if (q - name > longest) { 424 longest = q - name; 425 nmatches = 1; 426 found = c; 427 } else if (q - name == longest) 428 nmatches++; 429 } 430 } 431 if (nmatches > 1) 432 return ((struct cmd *)-1); 433 return (found); 434 } 435 436 /* 437 * Slice a string up into argc/argv. 438 */ 439 440 static int slrflag; 441 #define MARGV_INC 20 442 443 void 444 makeargv(void) 445 { 446 char **argp; 447 static int margv_size; 448 449 margc = 0; 450 stringbase = line; /* scan from first of buffer */ 451 argbase = argbuf; /* store from first of buffer */ 452 slrflag = 0; 453 454 if (!margv) { 455 margv_size = MARGV_INC; 456 if ((margv = malloc(margv_size * sizeof (char *))) == NULL) 457 fatal("Out of memory"); 458 } 459 argp = margv; 460 while (*argp++ = slurpstring()) { 461 margc++; 462 if (margc == margv_size) { 463 margv_size += MARGV_INC; 464 if ((margv = realloc(margv, 465 margv_size * sizeof (char *))) == NULL) 466 fatal("Out of memory"); 467 argp = margv + margc; 468 } 469 } 470 } 471 472 /* 473 * Parse string into argbuf; 474 * implemented with FSM to 475 * handle quoting and strings 476 */ 477 static char * 478 slurpstring(void) 479 { 480 int got_one = 0; 481 char *sb = stringbase; 482 char *ap = argbase; 483 char *tmp = argbase; /* will return this if token found */ 484 int len; 485 486 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 487 switch (slrflag) { /* and $ as token for macro invoke */ 488 case 0: 489 slrflag++; 490 stringbase++; 491 return ((*sb == '!') ? "!" : "$"); 492 case 1: 493 slrflag++; 494 altarg = stringbase; 495 break; 496 default: 497 break; 498 } 499 } 500 501 S0: 502 switch (*sb) { 503 504 case '\0': 505 goto OUT; 506 507 case ' ': 508 case '\t': 509 sb++; goto S0; 510 511 default: 512 switch (slrflag) { 513 case 0: 514 slrflag++; 515 break; 516 case 1: 517 slrflag++; 518 altarg = sb; 519 break; 520 default: 521 break; 522 } 523 goto S1; 524 } 525 526 S1: 527 switch (*sb) { 528 529 case ' ': 530 case '\t': 531 case '\0': 532 goto OUT; /* end of token */ 533 534 case '\\': 535 sb++; goto S2; /* slurp next character */ 536 537 case '"': 538 sb++; goto S3; /* slurp quoted string */ 539 540 default: 541 if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 542 len = 1; 543 memcpy(ap, sb, len); 544 ap += len; 545 sb += len; 546 got_one = 1; 547 goto S1; 548 } 549 550 S2: 551 switch (*sb) { 552 553 case '\0': 554 goto OUT; 555 556 default: 557 if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 558 len = 1; 559 memcpy(ap, sb, len); 560 ap += len; 561 sb += len; 562 got_one = 1; 563 goto S1; 564 } 565 566 S3: 567 switch (*sb) { 568 569 case '\0': 570 goto OUT; 571 572 case '"': 573 sb++; goto S1; 574 575 default: 576 if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 577 len = 1; 578 memcpy(ap, sb, len); 579 ap += len; 580 sb += len; 581 got_one = 1; 582 goto S3; 583 } 584 585 OUT: 586 if (got_one) 587 *ap++ = '\0'; 588 argbase = ap; /* update storage pointer */ 589 stringbase = sb; /* update scan pointer */ 590 if (got_one) { 591 return (tmp); 592 } 593 switch (slrflag) { 594 case 0: 595 slrflag++; 596 break; 597 case 1: 598 slrflag++; 599 altarg = (char *)0; 600 break; 601 default: 602 break; 603 } 604 return ((char *)0); 605 } 606 607 #define HELPINDENT (sizeof ("directory")) 608 609 /* 610 * Help command. 611 * Call each command handler with argc == 0 and argv[0] == name. 612 */ 613 void 614 help(int argc, char *argv[]) 615 { 616 struct cmd *c; 617 extern struct cmd cmdtab[]; 618 619 if (argc == 1) { 620 int i, j, w, k; 621 int columns, width = 0, lines; 622 extern int NCMDS; 623 624 (void) printf( 625 "Commands may be abbreviated. Commands are:\n\n"); 626 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 627 int len = strlen(c->c_name); 628 629 if (len > width) 630 width = len; 631 } 632 width = (width + 8) &~ 7; 633 columns = 80 / width; 634 if (columns == 0) 635 columns = 1; 636 lines = (NCMDS + columns - 1) / columns; 637 for (i = 0; i < lines; i++) { 638 for (j = 0; j < columns; j++) { 639 c = cmdtab + j * lines + i; 640 if (c->c_name && (!proxy || c->c_proxy)) { 641 (void) printf("%s", c->c_name); 642 } else if (c->c_name) { 643 for (k = 0; k < strlen(c->c_name); 644 k++) { 645 (void) putchar(' '); 646 } 647 } 648 if (c + lines >= &cmdtab[NCMDS]) { 649 (void) printf("\n"); 650 break; 651 } 652 w = strlen(c->c_name); 653 while (w < width) { 654 w = (w + 8) &~ 7; 655 (void) putchar('\t'); 656 } 657 } 658 } 659 return; 660 } 661 while (--argc > 0) { 662 char *arg; 663 arg = *++argv; 664 c = getcmd(arg); 665 if (c == (struct cmd *)-1) 666 (void) printf("?Ambiguous help command %s\n", arg); 667 else if (c == (struct cmd *)0) 668 (void) printf("?Invalid help command %s\n", arg); 669 else 670 (void) printf("%-*s\t%s\n", HELPINDENT, 671 c->c_name, c->c_help); 672 } 673 } 674 675 /* 676 * Call routine with argc, argv set from args (terminated by 0). 677 */ 678 void 679 call(void (*routine)(int argc, char *argv[]), ...) 680 { 681 va_list ap; 682 char *argv[10]; 683 int argc = 0; 684 685 va_start(ap, routine); 686 while ((argv[argc] = va_arg(ap, char *)) != (char *)0) 687 argc++; 688 va_end(ap); 689 (*routine)(argc, argv); 690 } 691