xref: /netbsd-src/usr.bin/ftp/main.c (revision 1f2744e6e4915c9da2a3f980279398c4cf7d5e6d)
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 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)main.c	8.4 (Berkeley) 4/3/94";*/
42 static char *rcsid = "$Id: main.c,v 1.7 1994/12/24 16:42:19 cgd Exp $";
43 #endif /* not lint */
44 
45 /*
46  * FTP User Program -- Command Interface.
47  */
48 /*#include <sys/ioctl.h>*/
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 
52 #include <arpa/ftp.h>
53 
54 #include <ctype.h>
55 #include <err.h>
56 #include <netdb.h>
57 #include <pwd.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "ftp_var.h"
65 
66 int
67 main(argc, argv)
68 	int argc;
69 	char *argv[];
70 {
71 	int ch, top;
72 	struct passwd *pw = NULL;
73 	char *cp, homedir[MAXPATHLEN];
74 
75 	sp = getservbyname("ftp", "tcp");
76 	if (sp == 0)
77 		errx(1, "ftp/tcp: unknown service");
78 	doglob = 1;
79 	interactive = 1;
80 	autologin = 1;
81 
82 	while ((ch = getopt(argc, argv, "dgintv")) != -1) {
83 		switch (ch) {
84 		case 'd':
85 			options |= SO_DEBUG;
86 			debug++;
87 			break;
88 
89 		case 'g':
90 			doglob = 0;
91 			break;
92 
93 		case 'i':
94 			interactive = 0;
95 			break;
96 
97 		case 'n':
98 			autologin = 0;
99 			break;
100 
101 		case 't':
102 			trace++;
103 			break;
104 
105 		case 'v':
106 			verbose++;
107 			break;
108 
109 		default:
110 			(void)fprintf(stderr,
111 				"usage: ftp [-dgintv] [host [port]]\n");
112 			exit(1);
113 		}
114 	}
115 	argc -= optind;
116 	argv += optind;
117 
118 	fromatty = isatty(fileno(stdin));
119 	if (fromatty)
120 		verbose++;
121 	cpend = 0;	/* no pending replies */
122 	proxy = 0;	/* proxy not active */
123 	passivemode = 0; /* passive mode not active */
124 	crflag = 1;	/* strip c.r. on ascii gets */
125 	sendport = -1;	/* not using ports */
126 	/*
127 	 * Set up the home directory in case we're globbing.
128 	 */
129 	cp = getlogin();
130 	if (cp != NULL) {
131 		pw = getpwnam(cp);
132 	}
133 	if (pw == NULL)
134 		pw = getpwuid(getuid());
135 	if (pw != NULL) {
136 		home = homedir;
137 		(void) strcpy(home, pw->pw_dir);
138 	}
139 	if (argc > 0) {
140 		char *xargv[5];
141 		extern char *__progname;
142 
143 		if (setjmp(toplevel))
144 			exit(0);
145 		(void) signal(SIGINT, intr);
146 		(void) signal(SIGPIPE, lostpeer);
147 		xargv[0] = __progname;
148 		xargv[1] = argv[0];
149 		xargv[2] = argv[1];
150 		xargv[3] = argv[2];
151 		xargv[4] = NULL;
152 		setpeer(argc+1, xargv);
153 	}
154 	top = setjmp(toplevel) == 0;
155 	if (top) {
156 		(void) signal(SIGINT, intr);
157 		(void) signal(SIGPIPE, lostpeer);
158 	}
159 	for (;;) {
160 		cmdscanner(top);
161 		top = 1;
162 	}
163 }
164 
165 void
166 intr()
167 {
168 
169 	longjmp(toplevel, 1);
170 }
171 
172 void
173 lostpeer()
174 {
175 
176 	if (connected) {
177 		if (cout != NULL) {
178 			(void) shutdown(fileno(cout), 1+1);
179 			(void) fclose(cout);
180 			cout = NULL;
181 		}
182 		if (data >= 0) {
183 			(void) shutdown(data, 1+1);
184 			(void) close(data);
185 			data = -1;
186 		}
187 		connected = 0;
188 	}
189 	pswitch(1);
190 	if (connected) {
191 		if (cout != NULL) {
192 			(void) shutdown(fileno(cout), 1+1);
193 			(void) fclose(cout);
194 			cout = NULL;
195 		}
196 		connected = 0;
197 	}
198 	proxflag = 0;
199 	pswitch(0);
200 }
201 
202 /*
203 char *
204 tail(filename)
205 	char *filename;
206 {
207 	char *s;
208 
209 	while (*filename) {
210 		s = strrchr(filename, '/');
211 		if (s == NULL)
212 			break;
213 		if (s[1])
214 			return (s + 1);
215 		*s = '\0';
216 	}
217 	return (filename);
218 }
219 */
220 
221 /*
222  * Command parser.
223  */
224 void
225 cmdscanner(top)
226 	int top;
227 {
228 	struct cmd *c;
229 	int l;
230 
231 	if (!top)
232 		(void) putchar('\n');
233 	for (;;) {
234 		if (fromatty) {
235 			printf("ftp> ");
236 			(void) fflush(stdout);
237 		}
238 		if (fgets(line, sizeof line, stdin) == NULL)
239 			quit(0, 0);
240 		l = strlen(line);
241 		if (l == 0)
242 			break;
243 		if (line[--l] == '\n') {
244 			if (l == 0)
245 				break;
246 			line[l] = '\0';
247 		} else if (l == sizeof(line) - 2) {
248 			printf("sorry, input line too long\n");
249 			while ((l = getchar()) != '\n' && l != EOF)
250 				/* void */;
251 			break;
252 		} /* else it was a line without a newline */
253 		makeargv();
254 		if (margc == 0) {
255 			continue;
256 		}
257 		c = getcmd(margv[0]);
258 		if (c == (struct cmd *)-1) {
259 			printf("?Ambiguous command\n");
260 			continue;
261 		}
262 		if (c == 0) {
263 			printf("?Invalid command\n");
264 			continue;
265 		}
266 		if (c->c_conn && !connected) {
267 			printf("Not connected.\n");
268 			continue;
269 		}
270 		(*c->c_handler)(margc, margv);
271 		if (bell && c->c_bell)
272 			(void) putchar('\007');
273 		if (c->c_handler != help)
274 			break;
275 	}
276 	(void) signal(SIGINT, intr);
277 	(void) signal(SIGPIPE, lostpeer);
278 }
279 
280 struct cmd *
281 getcmd(name)
282 	char *name;
283 {
284 	char *p, *q;
285 	struct cmd *c, *found;
286 	int nmatches, longest;
287 
288 	longest = 0;
289 	nmatches = 0;
290 	found = 0;
291 	for (c = cmdtab; p = c->c_name; c++) {
292 		for (q = name; *q == *p++; q++)
293 			if (*q == 0)		/* exact match? */
294 				return (c);
295 		if (!*q) {			/* the name was a prefix */
296 			if (q - name > longest) {
297 				longest = q - name;
298 				nmatches = 1;
299 				found = c;
300 			} else if (q - name == longest)
301 				nmatches++;
302 		}
303 	}
304 	if (nmatches > 1)
305 		return ((struct cmd *)-1);
306 	return (found);
307 }
308 
309 /*
310  * Slice a string up into argc/argv.
311  */
312 
313 int slrflag;
314 
315 void
316 makeargv()
317 {
318 	char **argp;
319 
320 	margc = 0;
321 	argp = margv;
322 	stringbase = line;		/* scan from first of buffer */
323 	argbase = argbuf;		/* store from first of buffer */
324 	slrflag = 0;
325 	while (*argp++ = slurpstring())
326 		margc++;
327 }
328 
329 /*
330  * Parse string into argbuf;
331  * implemented with FSM to
332  * handle quoting and strings
333  */
334 char *
335 slurpstring()
336 {
337 	int got_one = 0;
338 	char *sb = stringbase;
339 	char *ap = argbase;
340 	char *tmp = argbase;		/* will return this if token found */
341 
342 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
343 		switch (slrflag) {	/* and $ as token for macro invoke */
344 			case 0:
345 				slrflag++;
346 				stringbase++;
347 				return ((*sb == '!') ? "!" : "$");
348 				/* NOTREACHED */
349 			case 1:
350 				slrflag++;
351 				altarg = stringbase;
352 				break;
353 			default:
354 				break;
355 		}
356 	}
357 
358 S0:
359 	switch (*sb) {
360 
361 	case '\0':
362 		goto OUT;
363 
364 	case ' ':
365 	case '\t':
366 		sb++; goto S0;
367 
368 	default:
369 		switch (slrflag) {
370 			case 0:
371 				slrflag++;
372 				break;
373 			case 1:
374 				slrflag++;
375 				altarg = sb;
376 				break;
377 			default:
378 				break;
379 		}
380 		goto S1;
381 	}
382 
383 S1:
384 	switch (*sb) {
385 
386 	case ' ':
387 	case '\t':
388 	case '\0':
389 		goto OUT;	/* end of token */
390 
391 	case '\\':
392 		sb++; goto S2;	/* slurp next character */
393 
394 	case '"':
395 		sb++; goto S3;	/* slurp quoted string */
396 
397 	default:
398 		*ap++ = *sb++;	/* add character to token */
399 		got_one = 1;
400 		goto S1;
401 	}
402 
403 S2:
404 	switch (*sb) {
405 
406 	case '\0':
407 		goto OUT;
408 
409 	default:
410 		*ap++ = *sb++;
411 		got_one = 1;
412 		goto S1;
413 	}
414 
415 S3:
416 	switch (*sb) {
417 
418 	case '\0':
419 		goto OUT;
420 
421 	case '"':
422 		sb++; goto S1;
423 
424 	default:
425 		*ap++ = *sb++;
426 		got_one = 1;
427 		goto S3;
428 	}
429 
430 OUT:
431 	if (got_one)
432 		*ap++ = '\0';
433 	argbase = ap;			/* update storage pointer */
434 	stringbase = sb;		/* update scan pointer */
435 	if (got_one) {
436 		return (tmp);
437 	}
438 	switch (slrflag) {
439 		case 0:
440 			slrflag++;
441 			break;
442 		case 1:
443 			slrflag++;
444 			altarg = (char *) 0;
445 			break;
446 		default:
447 			break;
448 	}
449 	return ((char *)0);
450 }
451 
452 #define HELPINDENT ((int) sizeof ("directory"))
453 
454 /*
455  * Help command.
456  * Call each command handler with argc == 0 and argv[0] == name.
457  */
458 void
459 help(argc, argv)
460 	int argc;
461 	char *argv[];
462 {
463 	struct cmd *c;
464 
465 	if (argc == 1) {
466 		int i, j, w, k;
467 		int columns, width = 0, lines;
468 
469 		printf("Commands may be abbreviated.  Commands are:\n\n");
470 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
471 			int len = strlen(c->c_name);
472 
473 			if (len > width)
474 				width = len;
475 		}
476 		width = (width + 8) &~ 7;
477 		columns = 80 / width;
478 		if (columns == 0)
479 			columns = 1;
480 		lines = (NCMDS + columns - 1) / columns;
481 		for (i = 0; i < lines; i++) {
482 			for (j = 0; j < columns; j++) {
483 				c = cmdtab + j * lines + i;
484 				if (c->c_name && (!proxy || c->c_proxy)) {
485 					printf("%s", c->c_name);
486 				}
487 				else if (c->c_name) {
488 					for (k=0; k < strlen(c->c_name); k++) {
489 						(void) putchar(' ');
490 					}
491 				}
492 				if (c + lines >= &cmdtab[NCMDS]) {
493 					printf("\n");
494 					break;
495 				}
496 				w = strlen(c->c_name);
497 				while (w < width) {
498 					w = (w + 8) &~ 7;
499 					(void) putchar('\t');
500 				}
501 			}
502 		}
503 		return;
504 	}
505 	while (--argc > 0) {
506 		char *arg;
507 		arg = *++argv;
508 		c = getcmd(arg);
509 		if (c == (struct cmd *)-1)
510 			printf("?Ambiguous help command %s\n", arg);
511 		else if (c == (struct cmd *)0)
512 			printf("?Invalid help command %s\n", arg);
513 		else
514 			printf("%-*s\t%s\n", HELPINDENT,
515 				c->c_name, c->c_help);
516 	}
517 }
518