1/*
2 * Copyright (c) 1985, 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * FTP User Program -- Command Interface.
36 */
37
38#include "ftp_locl.h"
39#include <getarg.h>
40
41RCSID("$Id$");
42
43static int help_flag;
44static int version_flag;
45static int debug_flag;
46
47struct getargs getargs[] = {
48    { NULL,	'd', arg_flag, &debug_flag,
49      "debug", NULL },
50    { NULL,	'g', arg_negative_flag, &doglob,
51      "disables globbing", NULL},
52    { NULL,	'i', arg_negative_flag, &interactive,
53      "Turn off interactive prompting", NULL},
54    { NULL,	'l', arg_negative_flag, &lineedit,
55      "Turn off line editing", NULL},
56    { NULL,   'n', arg_negative_flag, &autologin,
57      "Turn off auto-login", NULL},
58    { NULL,	'p', arg_flag, &passivemode,
59      "passive mode", NULL},
60    { NULL,	't', arg_counter, &trace,
61      "Packet tracing", NULL},
62#ifdef KRB5
63    { "gss-bindings", 0,  arg_negative_flag, &ftp_do_gss_bindings,
64      "Don't use GSS-API bindings", NULL},
65    { "gss-delegate", 0,  arg_negative_flag, &ftp_do_gss_delegate,
66      "Disable delegation of GSS-API credentials", NULL},
67#endif
68    { NULL,	'v', arg_counter, &verbose,
69      "verbosity", NULL},
70    { NULL,	'K', arg_negative_flag, &use_kerberos,
71      "Disable kerberos authentication", NULL},
72    { "encrypt", 'x', arg_flag, &doencrypt,
73      "Encrypt command and data channel if possible" },
74    { "version", 0,  arg_flag, &version_flag },
75    { "help",	'h', arg_flag, &help_flag },
76};
77
78static int num_args = sizeof(getargs) / sizeof(getargs[0]);
79
80static void
81usage(int ecode)
82{
83    arg_printusage(getargs, num_args, NULL, "[host [port]]");
84    exit(ecode);
85}
86
87int
88main(int argc, char **argv)
89{
90	int top;
91	struct passwd *pw = NULL;
92	char homedir[MaxPathLen];
93	struct servent *sp;
94	int optind = 0;
95
96	setprogname(argv[0]);
97
98	sp = getservbyname("ftp", "tcp");
99	if (sp == 0)
100		errx(1, "ftp/tcp: unknown service");
101	doglob = 1;
102	interactive = 1;
103	autologin = 1;
104	lineedit = 1;
105	passivemode = 0; /* passive mode not active */
106        use_kerberos = 1;
107#ifdef KRB5
108	ftp_do_gss_bindings = 1;
109#endif
110
111	if(getarg(getargs, num_args, argc, argv, &optind))
112		usage(1);
113	if(help_flag)
114		usage(0);
115	if(version_flag) {
116		print_version(NULL);
117		exit(0);
118	}
119
120	if (debug_flag) {
121		options |= SO_DEBUG;
122		debug++;
123	}
124
125	argc -= optind;
126	argv += optind;
127
128	fromatty = isatty(fileno(stdin));
129	if (fromatty)
130		verbose++;
131	cpend = 0;	/* no pending replies */
132	proxy = 0;	/* proxy not active */
133	crflag = 1;	/* strip c.r. on ascii gets */
134	sendport = -1;	/* not using ports */
135	/*
136	 * Set up the home directory in case we're globbing.
137	 */
138	pw = k_getpwuid(getuid());
139	if (pw != NULL) {
140		strlcpy(homedir, pw->pw_dir, sizeof(homedir));
141		home = homedir;
142	}
143	if (argc > 0) {
144	    char *xargv[5];
145
146	    if (setjmp(toplevel))
147		exit(0);
148	    signal(SIGINT, intr);
149	    signal(SIGPIPE, lostpeer);
150	    xargv[0] = (char*)getprogname();
151	    xargv[1] = argv[0];
152	    xargv[2] = argv[1];
153	    xargv[3] = argv[2];
154	    xargv[4] = NULL;
155	    setpeer(argc+1, xargv);
156	}
157	if(setjmp(toplevel) == 0)
158	    top = 1;
159	else
160	    top = 0;
161	if (top) {
162	    signal(SIGINT, intr);
163	    signal(SIGPIPE, lostpeer);
164	}
165	for (;;) {
166	    cmdscanner(top);
167	    top = 1;
168	}
169}
170
171void
172intr(int sig)
173{
174
175	longjmp(toplevel, 1);
176}
177
178#ifndef SHUT_RDWR
179#define SHUT_RDWR 2
180#endif
181
182RETSIGTYPE
183lostpeer(int sig)
184{
185
186    if (connected) {
187	if (cout != NULL) {
188	    shutdown(fileno(cout), SHUT_RDWR);
189	    fclose(cout);
190	    cout = NULL;
191	}
192	if (data >= 0) {
193	    shutdown(data, SHUT_RDWR);
194	    close(data);
195	    data = -1;
196	}
197	connected = 0;
198    }
199    pswitch(1);
200    if (connected) {
201	if (cout != NULL) {
202	    shutdown(fileno(cout), SHUT_RDWR);
203	    fclose(cout);
204	    cout = NULL;
205	}
206	connected = 0;
207    }
208    proxflag = 0;
209    pswitch(0);
210    sec_end();
211    SIGRETURN(0);
212}
213
214/*
215char *
216tail(filename)
217	char *filename;
218{
219	char *s;
220
221	while (*filename) {
222		s = strrchr(filename, '/');
223		if (s == NULL)
224			break;
225		if (s[1])
226			return (s + 1);
227		*s = '\0';
228	}
229	return (filename);
230}
231*/
232
233static char *
234simple_readline(char *prompt)
235{
236    char buf[BUFSIZ];
237    printf ("%s", prompt);
238    fflush (stdout);
239    if(fgets(buf, sizeof(buf), stdin) == NULL)
240	return NULL;
241    if (buf[strlen(buf) - 1] == '\n')
242	buf[strlen(buf) - 1] = '\0';
243    return strdup(buf);
244}
245
246#ifndef HAVE_READLINE
247
248static char *
249readline(char *prompt)
250{
251    return simple_readline (prompt);
252}
253
254static void
255add_history(char *p)
256{
257}
258
259#else
260
261/* These should not really be here */
262
263char *readline(char *);
264void add_history(char *);
265
266#endif
267
268/*
269 * Command parser.
270 */
271void
272cmdscanner(int top)
273{
274    struct cmd *c;
275    int l;
276
277    if (!top)
278	putchar('\n');
279    for (;;) {
280	if (fromatty) {
281	    char *p;
282	    if (lineedit)
283		p = readline("ftp> ");
284	    else
285		p = simple_readline("ftp> ");
286	    if(p == NULL) {
287		printf("\n");
288		quit(0, 0);
289	    }
290	    strlcpy(line, p, sizeof(line));
291	    if (lineedit)
292		add_history(p);
293	    free(p);
294	} else{
295	    if (fgets(line, sizeof line, stdin) == NULL)
296		quit(0, 0);
297	}
298	/* XXX will break on long lines */
299	l = strlen(line);
300	if (l == 0)
301	    break;
302	if (line[--l] == '\n') {
303	    if (l == 0)
304		break;
305	    line[l] = '\0';
306	} else if (l == sizeof(line) - 2) {
307	    printf("sorry, input line too long\n");
308	    while ((l = getchar()) != '\n' && l != EOF)
309		/* void */;
310	    break;
311	} /* else it was a line without a newline */
312	makeargv();
313	if (margc == 0) {
314	    continue;
315	}
316	c = getcmd(margv[0]);
317	if (c == (struct cmd *)-1) {
318	    printf("?Ambiguous command\n");
319	    continue;
320	}
321	if (c == 0) {
322	    printf("?Invalid command\n");
323	    continue;
324	}
325	if (c->c_conn && !connected) {
326	    printf("Not connected.\n");
327	    continue;
328	}
329	(*c->c_handler)(margc, margv);
330	if (bell && c->c_bell)
331	    putchar('\007');
332	if (c->c_handler != help)
333	    break;
334    }
335    signal(SIGINT, intr);
336    signal(SIGPIPE, lostpeer);
337}
338
339struct cmd *
340getcmd(char *name)
341{
342	char *p, *q;
343	struct cmd *c, *found;
344	int nmatches, longest;
345
346	longest = 0;
347	nmatches = 0;
348	found = 0;
349	for (c = cmdtab; (p = c->c_name); c++) {
350		for (q = name; *q == *p++; q++)
351			if (*q == 0)		/* exact match? */
352				return (c);
353		if (!*q) {			/* the name was a prefix */
354			if (q - name > longest) {
355				longest = q - name;
356				nmatches = 1;
357				found = c;
358			} else if (q - name == longest)
359				nmatches++;
360		}
361	}
362	if (nmatches > 1)
363		return ((struct cmd *)-1);
364	return (found);
365}
366
367/*
368 * Slice a string up into argc/argv.
369 */
370
371int slrflag;
372
373void
374makeargv(void)
375{
376	char **argp;
377
378	argp = margv;
379	stringbase = line;		/* scan from first of buffer */
380	argbase = argbuf;		/* store from first of buffer */
381	slrflag = 0;
382	for (margc = 0; ; margc++) {
383		/* Expand array if necessary */
384		if (margc == margvlen) {
385			int i;
386
387			margv = (margvlen == 0)
388				? (char **)malloc(20 * sizeof(char *))
389				: (char **)realloc(margv,
390					(margvlen + 20)*sizeof(char *));
391			if (margv == NULL)
392				errx(1, "cannot realloc argv array");
393			for(i = margvlen; i < margvlen + 20; ++i)
394				margv[i] = NULL;
395			margvlen += 20;
396			argp = margv + margc;
397		}
398
399		if ((*argp++ = slurpstring()) == NULL)
400			break;
401	}
402
403}
404
405/*
406 * Parse string into argbuf;
407 * implemented with FSM to
408 * handle quoting and strings
409 */
410char *
411slurpstring(void)
412{
413	int got_one = 0;
414	char *sb = stringbase;
415	char *ap = argbase;
416	char *tmp = argbase;		/* will return this if token found */
417
418	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
419		switch (slrflag) {	/* and $ as token for macro invoke */
420			case 0:
421				slrflag++;
422				stringbase++;
423				return ((*sb == '!') ? "!" : "$");
424				/* NOTREACHED */
425			case 1:
426				slrflag++;
427				altarg = stringbase;
428				break;
429			default:
430				break;
431		}
432	}
433
434S0:
435	switch (*sb) {
436
437	case '\0':
438		goto OUT;
439
440	case ' ':
441	case '\t':
442		sb++; goto S0;
443
444	default:
445		switch (slrflag) {
446			case 0:
447				slrflag++;
448				break;
449			case 1:
450				slrflag++;
451				altarg = sb;
452				break;
453			default:
454				break;
455		}
456		goto S1;
457	}
458
459S1:
460	switch (*sb) {
461
462	case ' ':
463	case '\t':
464	case '\0':
465		goto OUT;	/* end of token */
466
467	case '\\':
468		sb++; goto S2;	/* slurp next character */
469
470	case '"':
471		sb++; goto S3;	/* slurp quoted string */
472
473	default:
474		*ap++ = *sb++;	/* add character to token */
475		got_one = 1;
476		goto S1;
477	}
478
479S2:
480	switch (*sb) {
481
482	case '\0':
483		goto OUT;
484
485	default:
486		*ap++ = *sb++;
487		got_one = 1;
488		goto S1;
489	}
490
491S3:
492	switch (*sb) {
493
494	case '\0':
495		goto OUT;
496
497	case '"':
498		sb++; goto S1;
499
500	default:
501		*ap++ = *sb++;
502		got_one = 1;
503		goto S3;
504	}
505
506OUT:
507	if (got_one)
508		*ap++ = '\0';
509	argbase = ap;			/* update storage pointer */
510	stringbase = sb;		/* update scan pointer */
511	if (got_one) {
512		return (tmp);
513	}
514	switch (slrflag) {
515		case 0:
516			slrflag++;
517			break;
518		case 1:
519			slrflag++;
520			altarg = (char *) 0;
521			break;
522		default:
523			break;
524	}
525	return NULL;
526}
527
528#define HELPINDENT ((int) sizeof ("directory"))
529
530/*
531 * Help command.
532 * Call each command handler with argc == 0 and argv[0] == name.
533 */
534void
535help(int argc, char **argv)
536{
537	struct cmd *c;
538
539	if (argc == 1) {
540		int i, j, w, k;
541		int columns, width = 0, lines;
542
543		printf("Commands may be abbreviated.  Commands are:\n\n");
544		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
545			int len = strlen(c->c_name);
546
547			if (len > width)
548				width = len;
549		}
550		width = (width + 8) &~ 7;
551		columns = 80 / width;
552		if (columns == 0)
553			columns = 1;
554		lines = (NCMDS + columns - 1) / columns;
555		for (i = 0; i < lines; i++) {
556			for (j = 0; j < columns; j++) {
557				c = cmdtab + j * lines + i;
558				if ((!proxy || c->c_proxy)) {
559					printf("%s", c->c_name);
560				} else {
561					for (k=0; k < strlen(c->c_name); k++) {
562						putchar(' ');
563					}
564				}
565				if (c + lines >= &cmdtab[NCMDS]) {
566					printf("\n");
567					break;
568				}
569				w = strlen(c->c_name);
570				while (w < width) {
571					w = (w + 8) &~ 7;
572					putchar('\t');
573				}
574			}
575		}
576		return;
577	}
578	while (--argc > 0) {
579		char *arg;
580		arg = *++argv;
581		c = getcmd(arg);
582		if (c == (struct cmd *)-1)
583			printf("?Ambiguous help command %s\n", arg);
584		else if (c == (struct cmd *)0)
585			printf("?Invalid help command %s\n", arg);
586		else
587			printf("%-*s\t%s\n", HELPINDENT,
588				c->c_name, c->c_help);
589	}
590}
591