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