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