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