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