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