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