Home | History | Annotate | Download | only in ftp
      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