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