Annotation of src/usr.bin/ftp/main.c, Revision 1.11
1.11 ! niklas 1: /* $OpenBSD: main.c,v 1.10 1996/12/18 01:59:15 michaels 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.11 ! niklas 46: static char rcsid[] = "$OpenBSD: main.c,v 1.10 1996/12/18 01:59:15 michaels 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.10 michaels 176: int xargc = 2, looping = 0, 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.10 michaels 193: if (strncmp(host, "ftp://", strlen("ftp://")) == 0) {
194: host += strlen("ftp://");
1.4 deraadt 195: p = strchr(host, '/');
1.10 michaels 196: }
197: else
1.4 deraadt 198: p = strchr(host, ':');
1.3 deraadt 199: *p = '\0';
1.4 deraadt 200:
201: xargv[1] = host;
202: xargc = 2;
1.3 deraadt 203: if (force_port) {
1.7 mickey 204: xargv[xargc++] = portstr;
1.3 deraadt 205: snprintf(portstr, sizeof portstr, "%d",
206: force_port);
207: }
1.7 mickey 208: xargv[xargc] = NULL;
1.10 michaels 209: setpeer(xargc, xargv);
1.3 deraadt 210: if (!connected) {
1.4 deraadt 211: printf("failed to connect to %s\n", host);
1.3 deraadt 212: ret = 1;
213: goto bail;
214: }
1.10 michaels 215: *argv = strchr(argv[0], ':') + 1;
216: do {
217: dir = *argv;
218: p = strrchr(dir, '/');
219: if (p != NULL) {
220: *p = '\0';
221: file = ++p;
222: } else {
223: file = dir;
224: dir = NULL;
225: }
226: if (dir != NULL && *dir != '\0') {
227: xargv[1] = dir;
228: xargv[2] = NULL;
229: xargc = 2;
230: cd(xargc, xargv);
231: }
232: xargv[1] = *file == '\0' ? "/" : file;
1.4 deraadt 233: xargv[2] = NULL;
234: xargc = 2;
1.10 michaels 235: tmp = verbose;
236: verbose = -1;
237: if (cd(xargc, xargv) == 0) {
238: verbose = tmp;
239: goto CLINE_CD;
240: }
1.9 michaels 241: verbose = tmp;
1.10 michaels 242: if (!looping) {
243: setbinary(NULL, 0);
244: looping = 1;
245: }
246: /* fetch file */
247: xargv[1] = file;
248: xargv[2] = NULL;
249: xargc = 2;
250: get(xargc, xargv);
1.11 ! niklas 251: if (code != 226)
! 252: ret = 1;
1.10 michaels 253: --argc;
254: argv++;
1.11 ! niklas 255: } while (argc > 0 && strchr(argv[0], ':') == NULL);
1.3 deraadt 256:
257: /* get ready for the next file */
258: bail:
1.6 deraadt 259: if (bufp) {
1.4 deraadt 260: free(bufp);
1.6 deraadt 261: bufp = NULL;
262: }
1.3 deraadt 263: if (connected)
264: disconnect(1, xargv);
265: }
266: exit(ret);
1.1 deraadt 267: }
268: if (argc > 0) {
269: char *xargv[5];
270: extern char *__progname;
271:
272: if (setjmp(toplevel))
273: exit(0);
274: (void) signal(SIGINT, intr);
275: (void) signal(SIGPIPE, lostpeer);
276: xargv[0] = __progname;
277: xargv[1] = argv[0];
278: xargv[2] = argv[1];
279: xargv[3] = argv[2];
280: xargv[4] = NULL;
1.7 mickey 281: do {
282: setpeer(argc+1, xargv);
283: if (!retry_connect)
284: break;
285: if (!connected) {
286: macnum = 0;
287: printf("Retrying...\n");
288: sleep(retry_connect);
289: }
290: } while (!connected);
1.1 deraadt 291: }
1.9 michaels 292: CLINE_CD:
1.1 deraadt 293: top = setjmp(toplevel) == 0;
294: if (top) {
295: (void) signal(SIGINT, intr);
296: (void) signal(SIGPIPE, lostpeer);
297: }
298: for (;;) {
299: cmdscanner(top);
300: top = 1;
301: }
302: }
303:
304: void
305: intr()
306: {
307:
308: longjmp(toplevel, 1);
309: }
310:
311: void
312: lostpeer()
313: {
314:
315: if (connected) {
316: if (cout != NULL) {
317: (void) shutdown(fileno(cout), 1+1);
318: (void) fclose(cout);
319: cout = NULL;
320: }
321: if (data >= 0) {
322: (void) shutdown(data, 1+1);
323: (void) close(data);
324: data = -1;
325: }
326: connected = 0;
327: }
328: pswitch(1);
329: if (connected) {
330: if (cout != NULL) {
331: (void) shutdown(fileno(cout), 1+1);
332: (void) fclose(cout);
333: cout = NULL;
334: }
335: connected = 0;
336: }
337: proxflag = 0;
338: pswitch(0);
339: }
340:
341: /*
342: char *
343: tail(filename)
344: char *filename;
345: {
346: char *s;
347:
348: while (*filename) {
349: s = strrchr(filename, '/');
350: if (s == NULL)
351: break;
352: if (s[1])
353: return (s + 1);
354: *s = '\0';
355: }
356: return (filename);
357: }
358: */
359:
360: /*
361: * Command parser.
362: */
363: void
364: cmdscanner(top)
365: int top;
366: {
367: struct cmd *c;
368: int l;
369:
370: if (!top)
371: (void) putchar('\n');
372: for (;;) {
373: if (fromatty) {
374: printf("ftp> ");
375: (void) fflush(stdout);
376: }
377: if (fgets(line, sizeof line, stdin) == NULL)
378: quit(0, 0);
379: l = strlen(line);
380: if (l == 0)
381: break;
382: if (line[--l] == '\n') {
383: if (l == 0)
384: break;
385: line[l] = '\0';
386: } else if (l == sizeof(line) - 2) {
387: printf("sorry, input line too long\n");
388: while ((l = getchar()) != '\n' && l != EOF)
389: /* void */;
390: break;
391: } /* else it was a line without a newline */
392: makeargv();
393: if (margc == 0) {
394: continue;
395: }
396: c = getcmd(margv[0]);
397: if (c == (struct cmd *)-1) {
398: printf("?Ambiguous command\n");
399: continue;
400: }
401: if (c == 0) {
402: printf("?Invalid command\n");
403: continue;
404: }
405: if (c->c_conn && !connected) {
406: printf("Not connected.\n");
407: continue;
408: }
409: (*c->c_handler)(margc, margv);
410: if (bell && c->c_bell)
411: (void) putchar('\007');
412: if (c->c_handler != help)
413: break;
414: }
415: (void) signal(SIGINT, intr);
416: (void) signal(SIGPIPE, lostpeer);
417: }
418:
419: struct cmd *
420: getcmd(name)
421: char *name;
422: {
423: char *p, *q;
424: struct cmd *c, *found;
425: int nmatches, longest;
1.2 deraadt 426:
427: if (name == NULL)
428: return (0);
1.1 deraadt 429:
430: longest = 0;
431: nmatches = 0;
432: found = 0;
433: for (c = cmdtab; p = c->c_name; c++) {
434: for (q = name; *q == *p++; q++)
435: if (*q == 0) /* exact match? */
436: return (c);
437: if (!*q) { /* the name was a prefix */
438: if (q - name > longest) {
439: longest = q - name;
440: nmatches = 1;
441: found = c;
442: } else if (q - name == longest)
443: nmatches++;
444: }
445: }
446: if (nmatches > 1)
447: return ((struct cmd *)-1);
448: return (found);
449: }
450:
451: /*
452: * Slice a string up into argc/argv.
453: */
454:
455: int slrflag;
456:
457: void
458: makeargv()
459: {
460: char **argp;
461:
462: argp = margv;
463: stringbase = line; /* scan from first of buffer */
464: argbase = argbuf; /* store from first of buffer */
465: slrflag = 0;
466: for (margc = 0; ; margc++) {
467: /* Expand array if necessary */
468: if (margc == margvlen) {
469: margv = (margvlen == 0)
470: ? (char **)malloc(20 * sizeof(char *))
471: : (char **)realloc(margv,
472: (margvlen + 20)*sizeof(char *));
473: if (margv == NULL)
474: errx(1, "cannot realloc argv array");
475: margvlen += 20;
476: argp = margv + margc;
477: }
478:
479: if ((*argp++ = slurpstring()) == NULL)
480: break;
481: }
482:
483: }
484:
485: /*
486: * Parse string into argbuf;
487: * implemented with FSM to
488: * handle quoting and strings
489: */
490: char *
491: slurpstring()
492: {
493: int got_one = 0;
494: char *sb = stringbase;
495: char *ap = argbase;
496: char *tmp = argbase; /* will return this if token found */
497:
498: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
499: switch (slrflag) { /* and $ as token for macro invoke */
500: case 0:
501: slrflag++;
502: stringbase++;
503: return ((*sb == '!') ? "!" : "$");
504: /* NOTREACHED */
505: case 1:
506: slrflag++;
507: altarg = stringbase;
508: break;
509: default:
510: break;
511: }
512: }
513:
514: S0:
515: switch (*sb) {
516:
517: case '\0':
518: goto OUT;
519:
520: case ' ':
521: case '\t':
522: sb++; goto S0;
523:
524: default:
525: switch (slrflag) {
526: case 0:
527: slrflag++;
528: break;
529: case 1:
530: slrflag++;
531: altarg = sb;
532: break;
533: default:
534: break;
535: }
536: goto S1;
537: }
538:
539: S1:
540: switch (*sb) {
541:
542: case ' ':
543: case '\t':
544: case '\0':
545: goto OUT; /* end of token */
546:
547: case '\\':
548: sb++; goto S2; /* slurp next character */
549:
550: case '"':
551: sb++; goto S3; /* slurp quoted string */
552:
553: default:
554: *ap++ = *sb++; /* add character to token */
555: got_one = 1;
556: goto S1;
557: }
558:
559: S2:
560: switch (*sb) {
561:
562: case '\0':
563: goto OUT;
564:
565: default:
566: *ap++ = *sb++;
567: got_one = 1;
568: goto S1;
569: }
570:
571: S3:
572: switch (*sb) {
573:
574: case '\0':
575: goto OUT;
576:
577: case '"':
578: sb++; goto S1;
579:
580: default:
581: *ap++ = *sb++;
582: got_one = 1;
583: goto S3;
584: }
585:
586: OUT:
587: if (got_one)
588: *ap++ = '\0';
589: argbase = ap; /* update storage pointer */
590: stringbase = sb; /* update scan pointer */
591: if (got_one) {
592: return (tmp);
593: }
594: switch (slrflag) {
595: case 0:
596: slrflag++;
597: break;
598: case 1:
599: slrflag++;
600: altarg = (char *) 0;
601: break;
602: default:
603: break;
604: }
605: return ((char *)0);
606: }
607:
608: #define HELPINDENT ((int) sizeof ("directory"))
609:
610: /*
611: * Help command.
612: * Call each command handler with argc == 0 and argv[0] == name.
613: */
614: void
615: help(argc, argv)
616: int argc;
617: char *argv[];
618: {
619: struct cmd *c;
620:
621: if (argc == 1) {
622: int i, j, w, k;
623: int columns, width = 0, lines;
624:
625: printf("Commands may be abbreviated. Commands are:\n\n");
626: for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
627: int len = strlen(c->c_name);
628:
629: if (len > width)
630: width = len;
631: }
632: width = (width + 8) &~ 7;
633: columns = 80 / width;
634: if (columns == 0)
635: columns = 1;
636: lines = (NCMDS + columns - 1) / columns;
637: for (i = 0; i < lines; i++) {
638: for (j = 0; j < columns; j++) {
639: c = cmdtab + j * lines + i;
640: if (c->c_name && (!proxy || c->c_proxy)) {
641: printf("%s", c->c_name);
642: }
643: else if (c->c_name) {
644: for (k=0; k < strlen(c->c_name); k++) {
645: (void) putchar(' ');
646: }
647: }
648: if (c + lines >= &cmdtab[NCMDS]) {
649: printf("\n");
650: break;
651: }
652: w = strlen(c->c_name);
653: while (w < width) {
654: w = (w + 8) &~ 7;
655: (void) putchar('\t');
656: }
657: }
658: }
659: return;
660: }
661: while (--argc > 0) {
662: char *arg;
663: arg = *++argv;
664: c = getcmd(arg);
665: if (c == (struct cmd *)-1)
666: printf("?Ambiguous help command %s\n", arg);
667: else if (c == (struct cmd *)0)
668: printf("?Invalid help command %s\n", arg);
669: else
670: printf("%-*s\t%s\n", HELPINDENT,
671: c->c_name, c->c_help);
672: }
673: }