Annotation of src/usr.bin/ftp/main.c, Revision 1.6
1.6 ! deraadt 1: /* $OpenBSD: main.c,v 1.5 1996/06/26 05:33:38 deraadt Exp $ */
1.5 deraadt 2:
1.1 deraadt 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: #ifndef lint
37: static char copyright[] =
38: "@(#) 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
1.6 ! deraadt 46: static char rcsid[] = "$OpenBSD: main.c,v 1.5 1996/06/26 05:33:38 deraadt Exp $";
1.1 deraadt 47: #endif
48: #endif /* not lint */
49:
50: /*
51: * FTP User Program -- Command Interface.
52: */
53: /*#include <sys/ioctl.h>*/
54: #include <sys/types.h>
1.6 ! deraadt 55: #include <sys/file.h>
1.1 deraadt 56: #include <sys/socket.h>
1.6 ! deraadt 57: #include <netinet/in.h>
1.1 deraadt 58:
59: #include <arpa/ftp.h>
60:
61: #include <ctype.h>
62: #include <err.h>
63: #include <netdb.h>
64: #include <pwd.h>
65: #include <signal.h>
66: #include <stdio.h>
67: #include <stdlib.h>
68: #include <string.h>
69: #include <unistd.h>
70:
71: #include "ftp_var.h"
72:
73: int
74: main(argc, argv)
75: int argc;
76: char *argv[];
77: {
78: int ch, top;
79: struct passwd *pw = NULL;
80: char *cp, homedir[MAXPATHLEN];
1.3 deraadt 81: int force_port = 0;
1.1 deraadt 82:
83: sp = getservbyname("ftp", "tcp");
84: if (sp == 0)
85: errx(1, "ftp/tcp: unknown service");
86: doglob = 1;
87: interactive = 1;
88: autologin = 1;
89:
1.3 deraadt 90: while ((ch = getopt(argc, argv, "p:dgintv")) != EOF) {
1.1 deraadt 91: switch (ch) {
92: case 'd':
93: options |= SO_DEBUG;
94: debug++;
95: break;
96:
97: case 'g':
98: doglob = 0;
99: break;
100:
101: case 'i':
102: interactive = 0;
103: break;
104:
105: case 'n':
106: autologin = 0;
107: break;
108:
1.3 deraadt 109: case 'p':
110: force_port = atoi(optarg);
111: break;
112:
1.1 deraadt 113: case 't':
114: trace++;
115: break;
116:
117: case 'v':
118: verbose++;
119: break;
120:
121: default:
122: (void)fprintf(stderr,
123: "usage: ftp [-dgintv] [host [port]]\n");
124: exit(1);
125: }
126: }
127: argc -= optind;
128: argv += optind;
129:
130: fromatty = isatty(fileno(stdin));
131: if (fromatty)
132: verbose++;
133: cpend = 0; /* no pending replies */
134: proxy = 0; /* proxy not active */
135: passivemode = 0; /* passive mode not active */
136: crflag = 1; /* strip c.r. on ascii gets */
137: sendport = -1; /* not using ports */
138: /*
139: * Set up the home directory in case we're globbing.
140: */
141: cp = getlogin();
142: if (cp != NULL) {
143: pw = getpwnam(cp);
144: }
145: if (pw == NULL)
146: pw = getpwuid(getuid());
147: if (pw != NULL) {
148: home = homedir;
149: (void) strcpy(home, pw->pw_dir);
1.3 deraadt 150: }
151: if (argc > 0 && strchr(argv[0], ':')) {
152: int ret = 0;
153: anonftp = 1;
154:
155: while (argc > 0 && strchr(argv[0], ':')) {
156: char *xargv[5];
157: extern char *__progname;
1.4 deraadt 158: char portstr[20], *p, *bufp = NULL;
159: char *host = NULL, *dir = NULL, *file = NULL;
1.3 deraadt 160: int xargc = 2;
161:
162: if (setjmp(toplevel))
163: exit(0);
164: (void) signal(SIGINT, intr);
165: (void) signal(SIGPIPE, lostpeer);
166: xargv[0] = __progname;
167:
1.4 deraadt 168: host = strdup(argv[0]);
169: if (host == NULL) {
170: ret = 1;
171: goto bail;
172: }
1.6 ! deraadt 173: if (!strncmp(host, "http://", strlen("http://"))) {
! 174: http_fetch(host);
! 175: goto bail;
! 176: }
1.4 deraadt 177: if (strncmp(host, "ftp://", sizeof("ftp://")-1) == 0) {
178: host += sizeof("ftp://") - 1;
179: p = strchr(host, '/');
180: } else
181: p = strchr(host, ':');
1.3 deraadt 182: *p = '\0';
1.4 deraadt 183: dir = ++p;
184: p = strrchr(p, '/');
185: if (p) {
186: *p = '\0';
187: file = ++p;
188: } else {
189: file = dir;
190: dir = NULL;
191: }
192:
193: xargv[1] = host;
1.3 deraadt 194: xargv[2] = NULL;
1.4 deraadt 195: xargc = 2;
1.3 deraadt 196: if (force_port) {
197: xargv[2] = portstr;
198: snprintf(portstr, sizeof portstr, "%d",
199: force_port);
200: xargc++;
201: }
202: setpeer(xargc, xargv);
203: if (!connected) {
1.4 deraadt 204: printf("failed to connect to %s\n", host);
1.3 deraadt 205: ret = 1;
206: goto bail;
207: }
208:
209: setbinary(NULL, 0);
210:
1.4 deraadt 211: if (dir) {
212: xargv[1] = dir;
213: xargv[2] = NULL;
214: xargc = 2;
215: cd(xargc, xargv);
1.3 deraadt 216: }
217:
218: /* fetch file */
1.4 deraadt 219: xargv[1] = file;
1.3 deraadt 220: xargv[2] = NULL;
221: xargc = 2;
222: get(xargc, xargv);
223:
224: /* get ready for the next file */
225: bail:
1.6 ! deraadt 226: if (bufp) {
1.4 deraadt 227: free(bufp);
1.6 ! deraadt 228: bufp = NULL;
! 229: }
1.3 deraadt 230: if (connected)
231: disconnect(1, xargv);
232: --argc;
233: argv++;
234: }
235: exit(ret);
1.1 deraadt 236: }
237: if (argc > 0) {
238: char *xargv[5];
239: extern char *__progname;
240:
241: if (setjmp(toplevel))
242: exit(0);
243: (void) signal(SIGINT, intr);
244: (void) signal(SIGPIPE, lostpeer);
245: xargv[0] = __progname;
246: xargv[1] = argv[0];
247: xargv[2] = argv[1];
248: xargv[3] = argv[2];
249: xargv[4] = NULL;
250: setpeer(argc+1, xargv);
251: }
252: top = setjmp(toplevel) == 0;
253: if (top) {
254: (void) signal(SIGINT, intr);
255: (void) signal(SIGPIPE, lostpeer);
256: }
257: for (;;) {
258: cmdscanner(top);
259: top = 1;
260: }
261: }
262:
263: void
264: intr()
265: {
266:
267: longjmp(toplevel, 1);
268: }
269:
270: void
271: lostpeer()
272: {
273:
274: if (connected) {
275: if (cout != NULL) {
276: (void) shutdown(fileno(cout), 1+1);
277: (void) fclose(cout);
278: cout = NULL;
279: }
280: if (data >= 0) {
281: (void) shutdown(data, 1+1);
282: (void) close(data);
283: data = -1;
284: }
285: connected = 0;
286: }
287: pswitch(1);
288: if (connected) {
289: if (cout != NULL) {
290: (void) shutdown(fileno(cout), 1+1);
291: (void) fclose(cout);
292: cout = NULL;
293: }
294: connected = 0;
295: }
296: proxflag = 0;
297: pswitch(0);
298: }
299:
300: /*
301: char *
302: tail(filename)
303: char *filename;
304: {
305: char *s;
306:
307: while (*filename) {
308: s = strrchr(filename, '/');
309: if (s == NULL)
310: break;
311: if (s[1])
312: return (s + 1);
313: *s = '\0';
314: }
315: return (filename);
316: }
317: */
318:
319: /*
320: * Command parser.
321: */
322: void
323: cmdscanner(top)
324: int top;
325: {
326: struct cmd *c;
327: int l;
328:
329: if (!top)
330: (void) putchar('\n');
331: for (;;) {
332: if (fromatty) {
333: printf("ftp> ");
334: (void) fflush(stdout);
335: }
336: if (fgets(line, sizeof line, stdin) == NULL)
337: quit(0, 0);
338: l = strlen(line);
339: if (l == 0)
340: break;
341: if (line[--l] == '\n') {
342: if (l == 0)
343: break;
344: line[l] = '\0';
345: } else if (l == sizeof(line) - 2) {
346: printf("sorry, input line too long\n");
347: while ((l = getchar()) != '\n' && l != EOF)
348: /* void */;
349: break;
350: } /* else it was a line without a newline */
351: makeargv();
352: if (margc == 0) {
353: continue;
354: }
355: c = getcmd(margv[0]);
356: if (c == (struct cmd *)-1) {
357: printf("?Ambiguous command\n");
358: continue;
359: }
360: if (c == 0) {
361: printf("?Invalid command\n");
362: continue;
363: }
364: if (c->c_conn && !connected) {
365: printf("Not connected.\n");
366: continue;
367: }
368: (*c->c_handler)(margc, margv);
369: if (bell && c->c_bell)
370: (void) putchar('\007');
371: if (c->c_handler != help)
372: break;
373: }
374: (void) signal(SIGINT, intr);
375: (void) signal(SIGPIPE, lostpeer);
376: }
377:
378: struct cmd *
379: getcmd(name)
380: char *name;
381: {
382: char *p, *q;
383: struct cmd *c, *found;
384: int nmatches, longest;
1.2 deraadt 385:
386: if (name == NULL)
387: return (0);
1.1 deraadt 388:
389: longest = 0;
390: nmatches = 0;
391: found = 0;
392: for (c = cmdtab; p = c->c_name; c++) {
393: for (q = name; *q == *p++; q++)
394: if (*q == 0) /* exact match? */
395: return (c);
396: if (!*q) { /* the name was a prefix */
397: if (q - name > longest) {
398: longest = q - name;
399: nmatches = 1;
400: found = c;
401: } else if (q - name == longest)
402: nmatches++;
403: }
404: }
405: if (nmatches > 1)
406: return ((struct cmd *)-1);
407: return (found);
408: }
409:
410: /*
411: * Slice a string up into argc/argv.
412: */
413:
414: int slrflag;
415:
416: void
417: makeargv()
418: {
419: char **argp;
420:
421: argp = margv;
422: stringbase = line; /* scan from first of buffer */
423: argbase = argbuf; /* store from first of buffer */
424: slrflag = 0;
425: for (margc = 0; ; margc++) {
426: /* Expand array if necessary */
427: if (margc == margvlen) {
428: margv = (margvlen == 0)
429: ? (char **)malloc(20 * sizeof(char *))
430: : (char **)realloc(margv,
431: (margvlen + 20)*sizeof(char *));
432: if (margv == NULL)
433: errx(1, "cannot realloc argv array");
434: margvlen += 20;
435: argp = margv + margc;
436: }
437:
438: if ((*argp++ = slurpstring()) == NULL)
439: break;
440: }
441:
442: }
443:
444: /*
445: * Parse string into argbuf;
446: * implemented with FSM to
447: * handle quoting and strings
448: */
449: char *
450: slurpstring()
451: {
452: int got_one = 0;
453: char *sb = stringbase;
454: char *ap = argbase;
455: char *tmp = argbase; /* will return this if token found */
456:
457: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
458: switch (slrflag) { /* and $ as token for macro invoke */
459: case 0:
460: slrflag++;
461: stringbase++;
462: return ((*sb == '!') ? "!" : "$");
463: /* NOTREACHED */
464: case 1:
465: slrflag++;
466: altarg = stringbase;
467: break;
468: default:
469: break;
470: }
471: }
472:
473: S0:
474: switch (*sb) {
475:
476: case '\0':
477: goto OUT;
478:
479: case ' ':
480: case '\t':
481: sb++; goto S0;
482:
483: default:
484: switch (slrflag) {
485: case 0:
486: slrflag++;
487: break;
488: case 1:
489: slrflag++;
490: altarg = sb;
491: break;
492: default:
493: break;
494: }
495: goto S1;
496: }
497:
498: S1:
499: switch (*sb) {
500:
501: case ' ':
502: case '\t':
503: case '\0':
504: goto OUT; /* end of token */
505:
506: case '\\':
507: sb++; goto S2; /* slurp next character */
508:
509: case '"':
510: sb++; goto S3; /* slurp quoted string */
511:
512: default:
513: *ap++ = *sb++; /* add character to token */
514: got_one = 1;
515: goto S1;
516: }
517:
518: S2:
519: switch (*sb) {
520:
521: case '\0':
522: goto OUT;
523:
524: default:
525: *ap++ = *sb++;
526: got_one = 1;
527: goto S1;
528: }
529:
530: S3:
531: switch (*sb) {
532:
533: case '\0':
534: goto OUT;
535:
536: case '"':
537: sb++; goto S1;
538:
539: default:
540: *ap++ = *sb++;
541: got_one = 1;
542: goto S3;
543: }
544:
545: OUT:
546: if (got_one)
547: *ap++ = '\0';
548: argbase = ap; /* update storage pointer */
549: stringbase = sb; /* update scan pointer */
550: if (got_one) {
551: return (tmp);
552: }
553: switch (slrflag) {
554: case 0:
555: slrflag++;
556: break;
557: case 1:
558: slrflag++;
559: altarg = (char *) 0;
560: break;
561: default:
562: break;
563: }
564: return ((char *)0);
565: }
566:
567: #define HELPINDENT ((int) sizeof ("directory"))
568:
569: /*
570: * Help command.
571: * Call each command handler with argc == 0 and argv[0] == name.
572: */
573: void
574: help(argc, argv)
575: int argc;
576: char *argv[];
577: {
578: struct cmd *c;
579:
580: if (argc == 1) {
581: int i, j, w, k;
582: int columns, width = 0, lines;
583:
584: printf("Commands may be abbreviated. Commands are:\n\n");
585: for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
586: int len = strlen(c->c_name);
587:
588: if (len > width)
589: width = len;
590: }
591: width = (width + 8) &~ 7;
592: columns = 80 / width;
593: if (columns == 0)
594: columns = 1;
595: lines = (NCMDS + columns - 1) / columns;
596: for (i = 0; i < lines; i++) {
597: for (j = 0; j < columns; j++) {
598: c = cmdtab + j * lines + i;
599: if (c->c_name && (!proxy || c->c_proxy)) {
600: printf("%s", c->c_name);
601: }
602: else if (c->c_name) {
603: for (k=0; k < strlen(c->c_name); k++) {
604: (void) putchar(' ');
605: }
606: }
607: if (c + lines >= &cmdtab[NCMDS]) {
608: printf("\n");
609: break;
610: }
611: w = strlen(c->c_name);
612: while (w < width) {
613: w = (w + 8) &~ 7;
614: (void) putchar('\t');
615: }
616: }
617: }
618: return;
619: }
620: while (--argc > 0) {
621: char *arg;
622: arg = *++argv;
623: c = getcmd(arg);
624: if (c == (struct cmd *)-1)
625: printf("?Ambiguous help command %s\n", arg);
626: else if (c == (struct cmd *)0)
627: printf("?Invalid help command %s\n", arg);
628: else
629: printf("%-*s\t%s\n", HELPINDENT,
630: c->c_name, c->c_help);
631: }
632: }