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