Annotation of src/usr.bin/ftp/main.c, Revision 1.3
1.1 deraadt 1: /*
2: * Copyright (c) 1985, 1989, 1993, 1994
3: * The Regents of the University of California. All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #ifndef lint
35: static char copyright[] =
36: "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
37: The Regents of the University of California. All rights reserved.\n";
38: #endif /* not lint */
39:
40: #ifndef lint
41: #if 0
42: static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
43: #else
1.2 deraadt 44: static char rcsid[] = "$NetBSD: main.c,v 1.11 1996/05/07 00:16:55 pk Exp $";
1.1 deraadt 45: #endif
46: #endif /* not lint */
47:
48: /*
49: * FTP User Program -- Command Interface.
50: */
51: /*#include <sys/ioctl.h>*/
52: #include <sys/types.h>
53: #include <sys/socket.h>
54:
55: #include <arpa/ftp.h>
56:
57: #include <ctype.h>
58: #include <err.h>
59: #include <netdb.h>
60: #include <pwd.h>
61: #include <signal.h>
62: #include <stdio.h>
63: #include <stdlib.h>
64: #include <string.h>
65: #include <unistd.h>
66:
67: #include "ftp_var.h"
68:
69: int
70: main(argc, argv)
71: int argc;
72: char *argv[];
73: {
74: int ch, top;
75: struct passwd *pw = NULL;
76: char *cp, homedir[MAXPATHLEN];
1.3 ! deraadt 77: int force_port = 0;
1.1 deraadt 78:
79: sp = getservbyname("ftp", "tcp");
80: if (sp == 0)
81: errx(1, "ftp/tcp: unknown service");
82: doglob = 1;
83: interactive = 1;
84: autologin = 1;
85:
1.3 ! deraadt 86: while ((ch = getopt(argc, argv, "p:dgintv")) != EOF) {
1.1 deraadt 87: switch (ch) {
88: case 'd':
89: options |= SO_DEBUG;
90: debug++;
91: break;
92:
93: case 'g':
94: doglob = 0;
95: break;
96:
97: case 'i':
98: interactive = 0;
99: break;
100:
101: case 'n':
102: autologin = 0;
103: break;
104:
1.3 ! deraadt 105: case 'p':
! 106: force_port = atoi(optarg);
! 107: break;
! 108:
1.1 deraadt 109: case 't':
110: trace++;
111: break;
112:
113: case 'v':
114: verbose++;
115: break;
116:
117: default:
118: (void)fprintf(stderr,
119: "usage: ftp [-dgintv] [host [port]]\n");
120: exit(1);
121: }
122: }
123: argc -= optind;
124: argv += optind;
125:
126: fromatty = isatty(fileno(stdin));
127: if (fromatty)
128: verbose++;
129: cpend = 0; /* no pending replies */
130: proxy = 0; /* proxy not active */
131: passivemode = 0; /* passive mode not active */
132: crflag = 1; /* strip c.r. on ascii gets */
133: sendport = -1; /* not using ports */
134: /*
135: * Set up the home directory in case we're globbing.
136: */
137: cp = getlogin();
138: if (cp != NULL) {
139: pw = getpwnam(cp);
140: }
141: if (pw == NULL)
142: pw = getpwuid(getuid());
143: if (pw != NULL) {
144: home = homedir;
145: (void) strcpy(home, pw->pw_dir);
1.3 ! deraadt 146: }
! 147: if (argc > 0 && strchr(argv[0], ':')) {
! 148: int ret = 0;
! 149: anonftp = 1;
! 150:
! 151: while (argc > 0 && strchr(argv[0], ':')) {
! 152: char *xargv[5];
! 153: extern char *__progname;
! 154: char portstr[20], *p, *bufp;
! 155: int xargc = 2;
! 156:
! 157: if (setjmp(toplevel))
! 158: exit(0);
! 159: (void) signal(SIGINT, intr);
! 160: (void) signal(SIGPIPE, lostpeer);
! 161: xargv[0] = __progname;
! 162:
! 163: /* connect to host */
! 164: bufp = xargv[1] = strdup(argv[0]);
! 165: p = strchr(xargv[1], ':');
! 166: *p = '\0';
! 167: xargv[2] = NULL;
! 168: if (force_port) {
! 169: xargv[2] = portstr;
! 170: snprintf(portstr, sizeof portstr, "%d",
! 171: force_port);
! 172: xargc++;
! 173: }
! 174: setpeer(xargc, xargv);
! 175: if (!connected) {
! 176: printf("failed to connect to %s\n", xargv[1]);
! 177: ret = 1;
! 178: goto bail;
! 179: }
! 180: free(bufp);
! 181:
! 182: setbinary(NULL, 0);
! 183:
! 184: /* cd into correct directory */
! 185: bufp = xargv[1] = strdup(argv[0]);
! 186: if (xargv[1] == NULL) {
! 187: ret = 1;
! 188: goto bail;
! 189: }
! 190: xargv[1] = strchr(xargv[1], ':');
! 191: xargv[1]++;
! 192: p = strrchr(xargv[1], '/');
! 193: if (p)
! 194: *p = '\0';
! 195: xargv[2] = NULL;
! 196: xargc = 2;
! 197: cd(xargc, xargv);
! 198: free(bufp);
! 199:
! 200: /* fetch file */
! 201: bufp = xargv[1] = strdup(argv[0]);
! 202: if (xargv[1] == NULL) {
! 203: ret = 1;
! 204: goto bail;
! 205: }
! 206: xargv[1] = strchr(xargv[1], ':');
! 207: xargv[1]++;
! 208: p = strrchr(xargv[1], '/');
! 209: if (p)
! 210: xargv[1] = p + 1;
! 211: xargv[2] = NULL;
! 212: xargc = 2;
! 213: get(xargc, xargv);
! 214: free(bufp);
! 215:
! 216: /* get ready for the next file */
! 217: bail:
! 218: if (connected)
! 219: disconnect(1, xargv);
! 220: --argc;
! 221: argv++;
! 222: }
! 223: exit(ret);
1.1 deraadt 224: }
225: if (argc > 0) {
226: char *xargv[5];
227: extern char *__progname;
228:
229: if (setjmp(toplevel))
230: exit(0);
231: (void) signal(SIGINT, intr);
232: (void) signal(SIGPIPE, lostpeer);
233: xargv[0] = __progname;
234: xargv[1] = argv[0];
235: xargv[2] = argv[1];
236: xargv[3] = argv[2];
237: xargv[4] = NULL;
238: setpeer(argc+1, xargv);
239: }
240: top = setjmp(toplevel) == 0;
241: if (top) {
242: (void) signal(SIGINT, intr);
243: (void) signal(SIGPIPE, lostpeer);
244: }
245: for (;;) {
246: cmdscanner(top);
247: top = 1;
248: }
249: }
250:
251: void
252: intr()
253: {
254:
255: longjmp(toplevel, 1);
256: }
257:
258: void
259: lostpeer()
260: {
261:
262: if (connected) {
263: if (cout != NULL) {
264: (void) shutdown(fileno(cout), 1+1);
265: (void) fclose(cout);
266: cout = NULL;
267: }
268: if (data >= 0) {
269: (void) shutdown(data, 1+1);
270: (void) close(data);
271: data = -1;
272: }
273: connected = 0;
274: }
275: pswitch(1);
276: if (connected) {
277: if (cout != NULL) {
278: (void) shutdown(fileno(cout), 1+1);
279: (void) fclose(cout);
280: cout = NULL;
281: }
282: connected = 0;
283: }
284: proxflag = 0;
285: pswitch(0);
286: }
287:
288: /*
289: char *
290: tail(filename)
291: char *filename;
292: {
293: char *s;
294:
295: while (*filename) {
296: s = strrchr(filename, '/');
297: if (s == NULL)
298: break;
299: if (s[1])
300: return (s + 1);
301: *s = '\0';
302: }
303: return (filename);
304: }
305: */
306:
307: /*
308: * Command parser.
309: */
310: void
311: cmdscanner(top)
312: int top;
313: {
314: struct cmd *c;
315: int l;
316:
317: if (!top)
318: (void) putchar('\n');
319: for (;;) {
320: if (fromatty) {
321: printf("ftp> ");
322: (void) fflush(stdout);
323: }
324: if (fgets(line, sizeof line, stdin) == NULL)
325: quit(0, 0);
326: l = strlen(line);
327: if (l == 0)
328: break;
329: if (line[--l] == '\n') {
330: if (l == 0)
331: break;
332: line[l] = '\0';
333: } else if (l == sizeof(line) - 2) {
334: printf("sorry, input line too long\n");
335: while ((l = getchar()) != '\n' && l != EOF)
336: /* void */;
337: break;
338: } /* else it was a line without a newline */
339: makeargv();
340: if (margc == 0) {
341: continue;
342: }
343: c = getcmd(margv[0]);
344: if (c == (struct cmd *)-1) {
345: printf("?Ambiguous command\n");
346: continue;
347: }
348: if (c == 0) {
349: printf("?Invalid command\n");
350: continue;
351: }
352: if (c->c_conn && !connected) {
353: printf("Not connected.\n");
354: continue;
355: }
356: (*c->c_handler)(margc, margv);
357: if (bell && c->c_bell)
358: (void) putchar('\007');
359: if (c->c_handler != help)
360: break;
361: }
362: (void) signal(SIGINT, intr);
363: (void) signal(SIGPIPE, lostpeer);
364: }
365:
366: struct cmd *
367: getcmd(name)
368: char *name;
369: {
370: char *p, *q;
371: struct cmd *c, *found;
372: int nmatches, longest;
1.2 deraadt 373:
374: if (name == NULL)
375: return (0);
1.1 deraadt 376:
377: longest = 0;
378: nmatches = 0;
379: found = 0;
380: for (c = cmdtab; p = c->c_name; c++) {
381: for (q = name; *q == *p++; q++)
382: if (*q == 0) /* exact match? */
383: return (c);
384: if (!*q) { /* the name was a prefix */
385: if (q - name > longest) {
386: longest = q - name;
387: nmatches = 1;
388: found = c;
389: } else if (q - name == longest)
390: nmatches++;
391: }
392: }
393: if (nmatches > 1)
394: return ((struct cmd *)-1);
395: return (found);
396: }
397:
398: /*
399: * Slice a string up into argc/argv.
400: */
401:
402: int slrflag;
403:
404: void
405: makeargv()
406: {
407: char **argp;
408:
409: argp = margv;
410: stringbase = line; /* scan from first of buffer */
411: argbase = argbuf; /* store from first of buffer */
412: slrflag = 0;
413: for (margc = 0; ; margc++) {
414: /* Expand array if necessary */
415: if (margc == margvlen) {
416: margv = (margvlen == 0)
417: ? (char **)malloc(20 * sizeof(char *))
418: : (char **)realloc(margv,
419: (margvlen + 20)*sizeof(char *));
420: if (margv == NULL)
421: errx(1, "cannot realloc argv array");
422: margvlen += 20;
423: argp = margv + margc;
424: }
425:
426: if ((*argp++ = slurpstring()) == NULL)
427: break;
428: }
429:
430: }
431:
432: /*
433: * Parse string into argbuf;
434: * implemented with FSM to
435: * handle quoting and strings
436: */
437: char *
438: slurpstring()
439: {
440: int got_one = 0;
441: char *sb = stringbase;
442: char *ap = argbase;
443: char *tmp = argbase; /* will return this if token found */
444:
445: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
446: switch (slrflag) { /* and $ as token for macro invoke */
447: case 0:
448: slrflag++;
449: stringbase++;
450: return ((*sb == '!') ? "!" : "$");
451: /* NOTREACHED */
452: case 1:
453: slrflag++;
454: altarg = stringbase;
455: break;
456: default:
457: break;
458: }
459: }
460:
461: S0:
462: switch (*sb) {
463:
464: case '\0':
465: goto OUT;
466:
467: case ' ':
468: case '\t':
469: sb++; goto S0;
470:
471: default:
472: switch (slrflag) {
473: case 0:
474: slrflag++;
475: break;
476: case 1:
477: slrflag++;
478: altarg = sb;
479: break;
480: default:
481: break;
482: }
483: goto S1;
484: }
485:
486: S1:
487: switch (*sb) {
488:
489: case ' ':
490: case '\t':
491: case '\0':
492: goto OUT; /* end of token */
493:
494: case '\\':
495: sb++; goto S2; /* slurp next character */
496:
497: case '"':
498: sb++; goto S3; /* slurp quoted string */
499:
500: default:
501: *ap++ = *sb++; /* add character to token */
502: got_one = 1;
503: goto S1;
504: }
505:
506: S2:
507: switch (*sb) {
508:
509: case '\0':
510: goto OUT;
511:
512: default:
513: *ap++ = *sb++;
514: got_one = 1;
515: goto S1;
516: }
517:
518: S3:
519: switch (*sb) {
520:
521: case '\0':
522: goto OUT;
523:
524: case '"':
525: sb++; goto S1;
526:
527: default:
528: *ap++ = *sb++;
529: got_one = 1;
530: goto S3;
531: }
532:
533: OUT:
534: if (got_one)
535: *ap++ = '\0';
536: argbase = ap; /* update storage pointer */
537: stringbase = sb; /* update scan pointer */
538: if (got_one) {
539: return (tmp);
540: }
541: switch (slrflag) {
542: case 0:
543: slrflag++;
544: break;
545: case 1:
546: slrflag++;
547: altarg = (char *) 0;
548: break;
549: default:
550: break;
551: }
552: return ((char *)0);
553: }
554:
555: #define HELPINDENT ((int) sizeof ("directory"))
556:
557: /*
558: * Help command.
559: * Call each command handler with argc == 0 and argv[0] == name.
560: */
561: void
562: help(argc, argv)
563: int argc;
564: char *argv[];
565: {
566: struct cmd *c;
567:
568: if (argc == 1) {
569: int i, j, w, k;
570: int columns, width = 0, lines;
571:
572: printf("Commands may be abbreviated. Commands are:\n\n");
573: for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
574: int len = strlen(c->c_name);
575:
576: if (len > width)
577: width = len;
578: }
579: width = (width + 8) &~ 7;
580: columns = 80 / width;
581: if (columns == 0)
582: columns = 1;
583: lines = (NCMDS + columns - 1) / columns;
584: for (i = 0; i < lines; i++) {
585: for (j = 0; j < columns; j++) {
586: c = cmdtab + j * lines + i;
587: if (c->c_name && (!proxy || c->c_proxy)) {
588: printf("%s", c->c_name);
589: }
590: else if (c->c_name) {
591: for (k=0; k < strlen(c->c_name); k++) {
592: (void) putchar(' ');
593: }
594: }
595: if (c + lines >= &cmdtab[NCMDS]) {
596: printf("\n");
597: break;
598: }
599: w = strlen(c->c_name);
600: while (w < width) {
601: w = (w + 8) &~ 7;
602: (void) putchar('\t');
603: }
604: }
605: }
606: return;
607: }
608: while (--argc > 0) {
609: char *arg;
610: arg = *++argv;
611: c = getcmd(arg);
612: if (c == (struct cmd *)-1)
613: printf("?Ambiguous help command %s\n", arg);
614: else if (c == (struct cmd *)0)
615: printf("?Invalid help command %s\n", arg);
616: else
617: printf("%-*s\t%s\n", HELPINDENT,
618: c->c_name, c->c_help);
619: }
620: }