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