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