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