Annotation of src/usr.bin/ftp/main.c, Revision 1.34
1.34 ! millert 1: /* $OpenBSD: main.c,v 1.33 1997/06/10 19:39:54 millert Exp $ */
! 2: /* $NetBSD: main.c,v 1.23 1997/07/20 09:45:58 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.34 ! millert 47: static char rcsid[] = "$OpenBSD: main.c,v 1.33 1997/06/10 19:39:54 millert 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:
1.34 ! millert 68: int main __P((int, char **));
! 69:
1.1 deraadt 70: int
71: main(argc, argv)
72: int argc;
73: char *argv[];
74: {
1.17 millert 75: struct servent *sp;
1.34 ! millert 76: int ch, top, rval;
! 77: long port;
1.1 deraadt 78: struct passwd *pw = NULL;
79: char *cp, homedir[MAXPATHLEN];
1.28 millert 80: int dumb_terminal = 0;
1.30 deraadt 81: int outfd = -1;
1.1 deraadt 82:
83: sp = getservbyname("ftp", "tcp");
84: if (sp == 0)
1.17 millert 85: ftpport = htons(FTP_PORT); /* good fallback */
86: else
87: ftpport = sp->s_port;
88: sp = getservbyname("http", "tcp");
89: if (sp == 0)
90: httpport = htons(HTTP_PORT); /* good fallback */
91: else
92: httpport = sp->s_port;
1.1 deraadt 93: doglob = 1;
94: interactive = 1;
95: autologin = 1;
1.17 millert 96: passivemode = 0;
97: preserve = 1;
98: verbose = 0;
99: progress = 0;
1.25 millert 100: #ifndef SMALL
1.22 millert 101: editing = 0;
1.28 millert 102: el = NULL;
103: hist = NULL;
1.22 millert 104: #endif
1.8 kstailey 105: mark = HASHBYTES;
1.17 millert 106: marg_sl = sl_init();
1.1 deraadt 107:
1.17 millert 108: cp = strrchr(argv[0], '/');
109: cp = (cp == NULL) ? argv[0] : cp + 1;
110: if (strcmp(cp, "pftp") == 0)
111: passivemode = 1;
112:
1.31 millert 113: cp = getenv("TERM");
114: dumb_terminal = (cp == NULL || !strcmp(cp, "dumb") ||
115: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
1.17 millert 116: fromatty = isatty(fileno(stdin));
1.22 millert 117: if (fromatty) {
1.17 millert 118: verbose = 1; /* verbose if from a tty */
1.25 millert 119: #ifndef SMALL
1.28 millert 120: if (!dumb_terminal)
1.34 ! millert 121: editing = 1; /* editing mode on if tty is usable */
1.22 millert 122: #endif
123: }
1.30 deraadt 124:
125: ttyout = stdout;
1.33 millert 126: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
1.34 ! millert 127: progress = 1; /* progress bar on if tty is usable */
1.32 deraadt 128:
129: if (!isatty(fileno(ttyout))) {
130: outfd = fileno(stdout);
1.30 deraadt 131: ttyout = stderr;
132: }
1.17 millert 133:
1.21 kstailey 134: while ((ch = getopt(argc, argv, "adeginpPr:tvV")) != -1) {
1.1 deraadt 135: switch (ch) {
1.17 millert 136: case 'a':
137: anonftp = 1;
138: break;
139:
1.1 deraadt 140: case 'd':
141: options |= SO_DEBUG;
142: debug++;
143: break;
1.17 millert 144:
1.28 millert 145: case 'e':
146: #ifndef SMALL
1.22 millert 147: editing = 0;
148: #endif
1.21 kstailey 149: break;
150:
1.1 deraadt 151: case 'g':
152: doglob = 0;
153: break;
154:
155: case 'i':
156: interactive = 0;
157: break;
158:
159: case 'n':
160: autologin = 0;
161: break;
162:
1.3 deraadt 163: case 'p':
1.17 millert 164: passivemode = 1;
1.3 deraadt 165: break;
166:
1.17 millert 167: case 'P':
1.34 ! millert 168: port = strtol(optarg, &cp, 10);
! 169: if (port < 1 || port > 0xffff || *cp != '\0')
1.18 millert 170: warnx("bad port number: %s (ignored)", optarg);
1.17 millert 171: else
1.34 ! millert 172: ftpport = htons((in_port_t)port);
1.7 mickey 173: break;
174:
1.18 millert 175: case 'r':
176: if (isdigit(*optarg))
177: retry_connect = atoi(optarg);
178: else
179: errx(1, "-r requires numeric argument");
180: break;
181:
1.1 deraadt 182: case 't':
1.17 millert 183: trace = 1;
1.1 deraadt 184: break;
185:
186: case 'v':
1.17 millert 187: verbose = 1;
188: break;
189:
190: case 'V':
191: verbose = 0;
1.1 deraadt 192: break;
193:
194: default:
1.17 millert 195: usage();
1.1 deraadt 196: }
197: }
198: argc -= optind;
199: argv += optind;
200:
201: cpend = 0; /* no pending replies */
202: proxy = 0; /* proxy not active */
203: crflag = 1; /* strip c.r. on ascii gets */
204: sendport = -1; /* not using ports */
205: /*
206: * Set up the home directory in case we're globbing.
207: */
208: cp = getlogin();
209: if (cp != NULL) {
210: pw = getpwnam(cp);
211: }
212: if (pw == NULL)
213: pw = getpwuid(getuid());
214: if (pw != NULL) {
215: home = homedir;
1.18 millert 216: (void)strcpy(home, pw->pw_dir);
1.3 deraadt 217: }
1.9 michaels 218:
1.17 millert 219: setttywidth(0);
1.18 millert 220: (void)signal(SIGWINCH, setttywidth);
1.17 millert 221:
1.34 ! millert 222: #ifdef __GNUC__ /* XXX: to shut up gcc warnings */
! 223: (void)&argc;
! 224: (void)&argv;
! 225: #endif
! 226:
1.17 millert 227: if (argc > 0) {
228: if (strchr(argv[0], ':') != NULL) {
229: anonftp = 1; /* Handle "automatic" transfers. */
1.30 deraadt 230: rval = auto_fetch(argc, argv, outfd);
1.17 millert 231: if (rval >= 0) /* -1 == connected and cd-ed */
232: exit(rval);
233: } else {
1.3 deraadt 234: char *xargv[5];
235:
236: if (setjmp(toplevel))
237: exit(0);
1.20 millert 238: (void)signal(SIGINT, (sig_t)intr);
239: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.3 deraadt 240: xargv[0] = __progname;
1.17 millert 241: xargv[1] = argv[0];
242: xargv[2] = argv[1];
243: xargv[3] = argv[2];
244: xargv[4] = NULL;
1.18 millert 245: do {
246: setpeer(argc+1, xargv);
247: if (!retry_connect)
248: break;
249: if (!connected) {
250: macnum = 0;
1.30 deraadt 251: fputs("Retrying...\n", ttyout);
1.18 millert 252: sleep(retry_connect);
253: }
254: } while (!connected);
1.3 deraadt 255: }
1.1 deraadt 256: }
1.28 millert 257: #ifndef SMALL
258: controlediting();
259: #endif /* !SMALL */
1.1 deraadt 260: top = setjmp(toplevel) == 0;
261: if (top) {
1.20 millert 262: (void)signal(SIGINT, (sig_t)intr);
263: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 264: }
265: for (;;) {
266: cmdscanner(top);
267: top = 1;
268: }
269: }
270:
271: void
272: intr()
273: {
274:
1.17 millert 275: alarmtimer(0);
1.1 deraadt 276: longjmp(toplevel, 1);
277: }
278:
279: void
280: lostpeer()
281: {
282:
1.17 millert 283: alarmtimer(0);
1.1 deraadt 284: if (connected) {
285: if (cout != NULL) {
1.18 millert 286: (void)shutdown(fileno(cout), 1+1);
287: (void)fclose(cout);
1.1 deraadt 288: cout = NULL;
289: }
290: if (data >= 0) {
1.18 millert 291: (void)shutdown(data, 1+1);
292: (void)close(data);
1.1 deraadt 293: data = -1;
294: }
295: connected = 0;
296: }
297: pswitch(1);
298: if (connected) {
299: if (cout != NULL) {
1.18 millert 300: (void)shutdown(fileno(cout), 1+1);
301: (void)fclose(cout);
1.1 deraadt 302: cout = NULL;
303: }
304: connected = 0;
305: }
306: proxflag = 0;
307: pswitch(0);
308: }
309:
310: /*
1.17 millert 311: * Generate a prompt
312: */
1.1 deraadt 313: char *
1.17 millert 314: prompt()
1.1 deraadt 315: {
1.17 millert 316: return ("ftp> ");
1.1 deraadt 317: }
318:
319: /*
320: * Command parser.
321: */
322: void
323: cmdscanner(top)
324: int top;
325: {
326: struct cmd *c;
1.17 millert 327: int num;
1.1 deraadt 328:
1.17 millert 329: if (!top
1.25 millert 330: #ifndef SMALL
1.17 millert 331: && !editing
1.25 millert 332: #endif /* !SMALL */
1.17 millert 333: )
1.30 deraadt 334: (void)putc('\n', ttyout);
1.1 deraadt 335: for (;;) {
1.25 millert 336: #ifndef SMALL
1.17 millert 337: if (!editing) {
1.25 millert 338: #endif /* !SMALL */
1.17 millert 339: if (fromatty) {
1.30 deraadt 340: fputs(prompt(), ttyout);
341: (void)fflush(ttyout);
1.17 millert 342: }
343: if (fgets(line, sizeof(line), stdin) == NULL)
344: quit(0, 0);
345: num = strlen(line);
346: if (num == 0)
347: break;
348: if (line[--num] == '\n') {
349: if (num == 0)
350: break;
351: line[num] = '\0';
352: } else if (num == sizeof(line) - 2) {
1.30 deraadt 353: fputs("sorry, input line too long.\n", ttyout);
1.17 millert 354: while ((num = getchar()) != '\n' && num != EOF)
355: /* void */;
356: break;
357: } /* else it was a line without a newline */
1.25 millert 358: #ifndef SMALL
1.17 millert 359: } else {
360: const char *buf;
361: cursor_pos = NULL;
362:
363: if ((buf = el_gets(el, &num)) == NULL || num == 0)
364: quit(0, 0);
365: if (line[--num] == '\n') {
366: if (num == 0)
367: break;
368: } else if (num >= sizeof(line)) {
1.30 deraadt 369: fputs("sorry, input line too long.\n", ttyout);
1.1 deraadt 370: break;
1.17 millert 371: }
1.34 ! millert 372: memcpy(line, buf, (size_t)num);
1.17 millert 373: line[num] = '\0';
374: history(hist, H_ENTER, buf);
375: }
1.25 millert 376: #endif /* !SMALL */
1.17 millert 377:
1.1 deraadt 378: makeargv();
1.17 millert 379: if (margc == 0)
1.1 deraadt 380: continue;
381: c = getcmd(margv[0]);
382: if (c == (struct cmd *)-1) {
1.30 deraadt 383: fputs("?Ambiguous command.\n", ttyout);
1.1 deraadt 384: continue;
385: }
386: if (c == 0) {
1.25 millert 387: #ifndef SMALL
1.24 millert 388: /*
389: * Give editline(3) a shot at unknown commands.
390: * XXX - bogus commands with a colon in
391: * them will not elicit an error.
392: */
393: if (el_parse(el, margc, margv) != 0)
1.25 millert 394: #endif /* !SMALL */
1.30 deraadt 395: fputs("?Invalid command.\n", ttyout);
1.1 deraadt 396: continue;
397: }
398: if (c->c_conn && !connected) {
1.30 deraadt 399: fputs("Not connected.\n", ttyout);
1.1 deraadt 400: continue;
401: }
1.17 millert 402: confirmrest = 0;
1.1 deraadt 403: (*c->c_handler)(margc, margv);
404: if (bell && c->c_bell)
1.30 deraadt 405: (void)putc('\007', ttyout);
1.1 deraadt 406: if (c->c_handler != help)
407: break;
408: }
1.20 millert 409: (void)signal(SIGINT, (sig_t)intr);
410: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 411: }
412:
413: struct cmd *
414: getcmd(name)
1.17 millert 415: const char *name;
1.1 deraadt 416: {
1.17 millert 417: const char *p, *q;
1.1 deraadt 418: struct cmd *c, *found;
419: int nmatches, longest;
1.2 deraadt 420:
421: if (name == NULL)
422: return (0);
1.1 deraadt 423:
424: longest = 0;
425: nmatches = 0;
426: found = 0;
1.17 millert 427: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 428: for (q = name; *q == *p++; q++)
429: if (*q == 0) /* exact match? */
430: return (c);
431: if (!*q) { /* the name was a prefix */
432: if (q - name > longest) {
433: longest = q - name;
434: nmatches = 1;
435: found = c;
436: } else if (q - name == longest)
437: nmatches++;
438: }
439: }
440: if (nmatches > 1)
441: return ((struct cmd *)-1);
442: return (found);
443: }
444:
445: /*
446: * Slice a string up into argc/argv.
447: */
448:
449: int slrflag;
450:
451: void
452: makeargv()
453: {
1.17 millert 454: char *argp;
1.1 deraadt 455:
456: stringbase = line; /* scan from first of buffer */
457: argbase = argbuf; /* store from first of buffer */
458: slrflag = 0;
1.17 millert 459: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 460: for (margc = 0; ; margc++) {
1.17 millert 461: argp = slurpstring();
462: sl_add(marg_sl, argp);
463: if (argp == NULL)
1.1 deraadt 464: break;
465: }
1.25 millert 466: #ifndef SMALL
1.17 millert 467: if (cursor_pos == line) {
468: cursor_argc = 0;
469: cursor_argo = 0;
470: } else if (cursor_pos != NULL) {
471: cursor_argc = margc;
472: cursor_argo = strlen(margv[margc-1]);
473: }
1.25 millert 474: #endif /* !SMALL */
1.17 millert 475: }
1.1 deraadt 476:
1.25 millert 477: #ifdef SMALL
1.17 millert 478: #define INC_CHKCURSOR(x) (x)++
1.25 millert 479: #else /* !SMALL */
1.17 millert 480: #define INC_CHKCURSOR(x) { (x)++ ; \
481: if (x == cursor_pos) { \
482: cursor_argc = margc; \
483: cursor_argo = ap-argbase; \
484: cursor_pos = NULL; \
485: } }
486:
1.25 millert 487: #endif /* !SMALL */
1.1 deraadt 488:
489: /*
490: * Parse string into argbuf;
491: * implemented with FSM to
492: * handle quoting and strings
493: */
494: char *
495: slurpstring()
496: {
497: int got_one = 0;
498: char *sb = stringbase;
499: char *ap = argbase;
500: char *tmp = argbase; /* will return this if token found */
501:
502: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
503: switch (slrflag) { /* and $ as token for macro invoke */
504: case 0:
505: slrflag++;
1.17 millert 506: INC_CHKCURSOR(stringbase);
1.1 deraadt 507: return ((*sb == '!') ? "!" : "$");
508: /* NOTREACHED */
509: case 1:
510: slrflag++;
511: altarg = stringbase;
512: break;
513: default:
514: break;
515: }
516: }
517:
518: S0:
519: switch (*sb) {
520:
521: case '\0':
522: goto OUT;
523:
524: case ' ':
525: case '\t':
1.17 millert 526: INC_CHKCURSOR(sb);
527: goto S0;
1.1 deraadt 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 '\\':
1.17 millert 553: INC_CHKCURSOR(sb);
554: goto S2; /* slurp next character */
1.1 deraadt 555:
556: case '"':
1.17 millert 557: INC_CHKCURSOR(sb);
558: goto S3; /* slurp quoted string */
1.1 deraadt 559:
560: default:
1.17 millert 561: *ap = *sb; /* add character to token */
562: ap++;
563: INC_CHKCURSOR(sb);
1.1 deraadt 564: got_one = 1;
565: goto S1;
566: }
567:
568: S2:
569: switch (*sb) {
570:
571: case '\0':
572: goto OUT;
573:
574: default:
1.17 millert 575: *ap = *sb;
576: ap++;
577: INC_CHKCURSOR(sb);
1.1 deraadt 578: got_one = 1;
579: goto S1;
580: }
581:
582: S3:
583: switch (*sb) {
584:
585: case '\0':
586: goto OUT;
587:
588: case '"':
1.17 millert 589: INC_CHKCURSOR(sb);
590: goto S1;
1.1 deraadt 591:
592: default:
1.17 millert 593: *ap = *sb;
594: ap++;
595: INC_CHKCURSOR(sb);
1.1 deraadt 596: got_one = 1;
597: goto S3;
598: }
599:
600: OUT:
601: if (got_one)
602: *ap++ = '\0';
603: argbase = ap; /* update storage pointer */
604: stringbase = sb; /* update scan pointer */
605: if (got_one) {
606: return (tmp);
607: }
608: switch (slrflag) {
609: case 0:
610: slrflag++;
611: break;
612: case 1:
613: slrflag++;
614: altarg = (char *) 0;
615: break;
616: default:
617: break;
618: }
619: return ((char *)0);
620: }
621:
622: /*
623: * Help command.
624: * Call each command handler with argc == 0 and argv[0] == name.
625: */
626: void
627: help(argc, argv)
628: int argc;
629: char *argv[];
630: {
631: struct cmd *c;
632:
633: if (argc == 1) {
1.17 millert 634: StringList *buf;
1.1 deraadt 635:
1.17 millert 636: buf = sl_init();
1.30 deraadt 637: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1.17 millert 638: proxy ? "Proxy c" : "C");
639: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
640: if (c->c_name && (!proxy || c->c_proxy))
641: sl_add(buf, c->c_name);
642: list_vertical(buf);
643: sl_free(buf, 0);
1.1 deraadt 644: return;
645: }
1.17 millert 646:
1.18 millert 647: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 648:
1.1 deraadt 649: while (--argc > 0) {
650: char *arg;
1.17 millert 651:
1.1 deraadt 652: arg = *++argv;
653: c = getcmd(arg);
654: if (c == (struct cmd *)-1)
1.30 deraadt 655: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1.1 deraadt 656: else if (c == (struct cmd *)0)
1.30 deraadt 657: fprintf(ttyout, "?Invalid help command %s\n", arg);
1.1 deraadt 658: else
1.30 deraadt 659: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1.1 deraadt 660: c->c_name, c->c_help);
661: }
1.17 millert 662: }
663:
664: void
665: usage()
666: {
667: (void)fprintf(stderr,
1.22 millert 668: "usage: %s [-adeginprtvV] [host [port]]\n"
1.17 millert 669: " %s host:path[/]\n"
670: " %s ftp://host[:port]/path[/]\n"
671: " %s http://host[:port]/file\n",
672: __progname, __progname, __progname, __progname);
673: exit(1);
1.1 deraadt 674: }