main.c revision 1.38 1 /* $NetBSD: main.c,v 1.38 1999/03/08 04:36:13 lukem Exp $ */
2
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 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) 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
46 __RCSID("$NetBSD: main.c,v 1.38 1999/03/08 04:36:13 lukem Exp $");
47 #endif
48 #endif /* not lint */
49
50 /*
51 * FTP User Program -- Command Interface.
52 */
53 #include <sys/types.h>
54 #include <sys/socket.h>
55
56 #include <err.h>
57 #include <netdb.h>
58 #include <pwd.h>
59 #include <signal.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 #include "ftp_var.h"
66 #include "pathnames.h"
67
68 #define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */
69 #define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */
70 #define NO_PROXY "no_proxy" /* env var with list of non-proxied
71 * hosts, comma or space separated */
72
73 int main __P((int, char **));
74
75 int
76 main(argc, argv)
77 int argc;
78 char *argv[];
79 {
80 struct servent *sp;
81 int ch, top, rval;
82 long port;
83 struct passwd *pw = NULL;
84 char *cp, *ep, homedir[MAXPATHLEN];
85 char *outfile = NULL;
86 int dumbterm;
87
88 sp = getservbyname("ftp", "tcp");
89 if (sp == 0)
90 ftpport = htons(FTP_PORT); /* good fallback */
91 else
92 ftpport = sp->s_port;
93 sp = getservbyname("http", "tcp");
94 if (sp == 0)
95 httpport = htons(HTTP_PORT); /* good fallback */
96 else
97 httpport = sp->s_port;
98 ftpproxy = getenv(FTP_PROXY);
99 httpproxy = getenv(HTTP_PROXY);
100 no_proxy = getenv(NO_PROXY);
101 gateport = 0;
102 cp = getenv("FTPSERVERPORT");
103 if (cp != NULL) {
104 port = strtol(cp, &ep, 10);
105 if (port < 1 || port > MAX_IN_PORT_T || *ep != '\0')
106 warnx("bad $FTPSERVERPORT port number: %s (ignored)",
107 cp);
108 else
109 gateport = htons(port);
110 }
111 if (gateport == 0) {
112 sp = getservbyname("ftpgate", "tcp");
113 if (sp == 0)
114 gateport = htons(GATE_PORT);
115 else
116 gateport = sp->s_port;
117 }
118 doglob = 1;
119 interactive = 1;
120 autologin = 1;
121 passivemode = 1;
122 activefallback = 1;
123 preserve = 1;
124 verbose = 0;
125 progress = 0;
126 gatemode = 0;
127 #ifndef SMALL
128 editing = 0;
129 el = NULL;
130 hist = NULL;
131 #endif
132 mark = HASHBYTES;
133 marg_sl = sl_init();
134 if ((tmpdir = getenv("TMPDIR")) == NULL)
135 tmpdir = _PATH_TMP;
136
137 /* Set default operation mode based on FTPMODE environment variable */
138 if ((cp = getenv("FTPMODE")) != NULL) {
139 if (strcmp(cp, "passive") == 0) {
140 passivemode = 1;
141 activefallback = 0;
142 } else if (strcmp(cp, "active") == 0) {
143 passivemode = 0;
144 activefallback = 0;
145 } else if (strcmp(cp, "gate") == 0) {
146 gatemode = 1;
147 } else if (strcmp(cp, "auto") == 0) {
148 passivemode = 1;
149 activefallback = 1;
150 } else
151 warnx("unknown $FTPMODE '%s'; using defaults", cp);
152 }
153
154 if (strcmp(__progname, "pftp") == 0) {
155 passivemode = 1;
156 activefallback = 0;
157 } else if (strcmp(__progname, "gate-ftp") == 0)
158 gatemode = 1;
159
160 gateserver = getenv("FTPSERVER");
161 if (gateserver == NULL || *gateserver == '\0')
162 gateserver = GATE_SERVER;
163 if (gatemode) {
164 if (*gateserver == '\0') {
165 warnx(
166 "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
167 gatemode = 0;
168 }
169 }
170
171 cp = getenv("TERM");
172 if (cp == NULL || strcmp(cp, "dumb") == 0)
173 dumbterm = 1;
174 else
175 dumbterm = 0;
176 fromatty = isatty(fileno(stdin));
177 ttyout = stdout;
178 if (isatty(fileno(ttyout))) {
179 verbose = 1; /* verbose if from a tty */
180 #ifndef SMALL
181 if (! dumbterm) {
182 editing = 1; /* editing mode on if tty is usable */
183 if (foregroundproc())
184 progress = 1; /* progress bar on if fg */
185 }
186 #endif
187 }
188
189 while ((ch = getopt(argc, argv, "Aadefgino:pP:r:tvV")) != -1) {
190 switch (ch) {
191 case 'A':
192 activefallback = 0;
193 passivemode = 0;
194 break;
195
196 case 'a':
197 anonftp = 1;
198 break;
199
200 case 'd':
201 options |= SO_DEBUG;
202 debug++;
203 break;
204
205 case 'e':
206 #ifndef SMALL
207 editing = 0;
208 #endif
209 break;
210
211 case 'f':
212 flushcache = 1;
213 break;
214
215 case 'g':
216 doglob = 0;
217 break;
218
219 case 'i':
220 interactive = 0;
221 break;
222
223 case 'n':
224 autologin = 0;
225 break;
226
227 case 'o':
228 outfile = optarg;
229 if (strcmp(outfile, "-") == 0)
230 ttyout = stderr;
231 break;
232
233 case 'p':
234 passivemode = 1;
235 activefallback = 0;
236 break;
237
238 case 'P':
239 port = strtol(optarg, &ep, 10);
240 if (port < 1 || port > MAX_IN_PORT_T || *ep != '\0')
241 warnx("bad port number: %s (ignored)", optarg);
242 else
243 ftpport = htons((in_port_t)port);
244 break;
245
246 case 'r':
247 retry_connect = strtol(optarg, &ep, 10);
248 if (retry_connect < 1 || retry_connect > MAX_IN_PORT_T
249 || *ep != '\0')
250 errx(1, "bad retry value: %s", optarg);
251 break;
252
253 case 't':
254 trace = 1;
255 break;
256
257 case 'v':
258 progress = verbose = 1;
259 break;
260
261 case 'V':
262 progress = verbose = 0;
263 break;
264
265 default:
266 usage();
267 }
268 }
269 /* set line buffering on ttyout */
270 setvbuf(ttyout, NULL, _IOLBF, 0);
271 argc -= optind;
272 argv += optind;
273
274 cpend = 0; /* no pending replies */
275 proxy = 0; /* proxy not active */
276 crflag = 1; /* strip c.r. on ascii gets */
277 sendport = -1; /* not using ports */
278 /*
279 * Set up the home directory in case we're globbing.
280 */
281 cp = getlogin();
282 if (cp != NULL) {
283 pw = getpwnam(cp);
284 }
285 if (pw == NULL)
286 pw = getpwuid(getuid());
287 if (pw != NULL) {
288 home = homedir;
289 (void)strcpy(home, pw->pw_dir);
290 }
291
292 setttywidth(0);
293 (void)xsignal(SIGWINCH, setttywidth);
294
295 #ifdef __GNUC__ /* to shut up gcc warnings */
296 (void)&argc;
297 (void)&argv;
298 #endif
299
300 if (argc > 0) {
301 if (strchr(argv[0], ':') != NULL) {
302 rval = auto_fetch(argc, argv, outfile);
303 if (rval >= 0) /* -1 == connected and cd-ed */
304 exit(rval);
305 } else {
306 char *xargv[5];
307
308 if (setjmp(toplevel))
309 exit(0);
310 (void)signal(SIGINT, (sig_t)intr);
311 (void)signal(SIGPIPE, (sig_t)lostpeer);
312 xargv[0] = __progname;
313 xargv[1] = argv[0];
314 xargv[2] = argv[1];
315 xargv[3] = argv[2];
316 xargv[4] = NULL;
317 do {
318 setpeer(argc+1, xargv);
319 if (!retry_connect)
320 break;
321 if (!connected) {
322 macnum = 0;
323 fprintf(ttyout,
324 "Retrying in %d seconds...\n",
325 retry_connect);
326 sleep(retry_connect);
327 }
328 } while (!connected);
329 retry_connect = 0; /* connected, stop hiding msgs */
330 }
331 }
332 #ifndef SMALL
333 controlediting();
334 #endif /* !SMALL */
335 top = setjmp(toplevel) == 0;
336 if (top) {
337 (void)signal(SIGINT, (sig_t)intr);
338 (void)signal(SIGPIPE, (sig_t)lostpeer);
339 }
340 for (;;) {
341 cmdscanner(top);
342 top = 1;
343 }
344 }
345
346 void
347 intr()
348 {
349
350 alarmtimer(0);
351 longjmp(toplevel, 1);
352 }
353
354 void
355 lostpeer()
356 {
357
358 alarmtimer(0);
359 if (connected) {
360 if (cout != NULL) {
361 (void)shutdown(fileno(cout), 1+1);
362 (void)fclose(cout);
363 cout = NULL;
364 }
365 if (data >= 0) {
366 (void)shutdown(data, 1+1);
367 (void)close(data);
368 data = -1;
369 }
370 connected = 0;
371 }
372 pswitch(1);
373 if (connected) {
374 if (cout != NULL) {
375 (void)shutdown(fileno(cout), 1+1);
376 (void)fclose(cout);
377 cout = NULL;
378 }
379 connected = 0;
380 }
381 proxflag = 0;
382 pswitch(0);
383 }
384
385 /*
386 * Generate a prompt
387 */
388 char *
389 prompt()
390 {
391 return ("ftp> ");
392 }
393
394 /*
395 * Command parser.
396 */
397 void
398 cmdscanner(top)
399 int top;
400 {
401 struct cmd *c;
402 int num;
403
404 if (!top
405 #ifndef SMALL
406 && !editing
407 #endif /* !SMALL */
408 )
409 (void)putc('\n', ttyout);
410 for (;;) {
411 #ifndef SMALL
412 if (!editing) {
413 #endif /* !SMALL */
414 if (fromatty) {
415 fputs(prompt(), ttyout);
416 (void)fflush(ttyout);
417 }
418 if (fgets(line, sizeof(line), stdin) == NULL)
419 quit(0, 0);
420 num = strlen(line);
421 if (num == 0)
422 break;
423 if (line[--num] == '\n') {
424 if (num == 0)
425 break;
426 line[num] = '\0';
427 } else if (num == sizeof(line) - 2) {
428 fputs("sorry, input line too long.\n", ttyout);
429 while ((num = getchar()) != '\n' && num != EOF)
430 /* void */;
431 break;
432 } /* else it was a line without a newline */
433 #ifndef SMALL
434 } else {
435 const char *buf;
436 HistEvent ev;
437 cursor_pos = NULL;
438
439 if ((buf = el_gets(el, &num)) == NULL || num == 0)
440 quit(0, 0);
441 if (line[--num] == '\n') {
442 if (num == 0)
443 break;
444 } else if (num >= sizeof(line)) {
445 fputs("sorry, input line too long.\n", ttyout);
446 break;
447 }
448 memcpy(line, buf, num);
449 line[num] = '\0';
450 history(hist, &ev, H_ENTER, buf);
451 }
452 #endif /* !SMALL */
453
454 makeargv();
455 if (margc == 0)
456 continue;
457 c = getcmd(margv[0]);
458 if (c == (struct cmd *)-1) {
459 fputs("?Ambiguous command.\n", ttyout);
460 continue;
461 }
462 if (c == NULL) {
463 #if !defined(SMALL)
464 /*
465 * attempt to el_parse() unknown commands.
466 * any command containing a ':' would be parsed
467 * as "[prog:]cmd ...", and will result in a
468 * false positive if prog != "ftp", so treat
469 * such commands as invalid.
470 */
471 if (strchr(margv[0], ':') != NULL ||
472 el_parse(el, margc, margv) != 0)
473 #endif /* !SMALL */
474 fputs("?Invalid command.\n", ttyout);
475 continue;
476 }
477 if (c->c_conn && !connected) {
478 fputs("Not connected.\n", ttyout);
479 continue;
480 }
481 confirmrest = 0;
482 (*c->c_handler)(margc, margv);
483 if (bell && c->c_bell)
484 (void)putc('\007', ttyout);
485 if (c->c_handler != help)
486 break;
487 }
488 (void)signal(SIGINT, (sig_t)intr);
489 (void)signal(SIGPIPE, (sig_t)lostpeer);
490 }
491
492 struct cmd *
493 getcmd(name)
494 const char *name;
495 {
496 const char *p, *q;
497 struct cmd *c, *found;
498 int nmatches, longest;
499
500 if (name == NULL)
501 return (0);
502
503 longest = 0;
504 nmatches = 0;
505 found = 0;
506 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
507 for (q = name; *q == *p++; q++)
508 if (*q == 0) /* exact match? */
509 return (c);
510 if (!*q) { /* the name was a prefix */
511 if (q - name > longest) {
512 longest = q - name;
513 nmatches = 1;
514 found = c;
515 } else if (q - name == longest)
516 nmatches++;
517 }
518 }
519 if (nmatches > 1)
520 return ((struct cmd *)-1);
521 return (found);
522 }
523
524 /*
525 * Slice a string up into argc/argv.
526 */
527
528 int slrflag;
529
530 void
531 makeargv()
532 {
533 char *argp;
534
535 stringbase = line; /* scan from first of buffer */
536 argbase = argbuf; /* store from first of buffer */
537 slrflag = 0;
538 marg_sl->sl_cur = 0; /* reset to start of marg_sl */
539 for (margc = 0; ; margc++) {
540 argp = slurpstring();
541 sl_add(marg_sl, argp);
542 if (argp == NULL)
543 break;
544 }
545 #ifndef SMALL
546 if (cursor_pos == line) {
547 cursor_argc = 0;
548 cursor_argo = 0;
549 } else if (cursor_pos != NULL) {
550 cursor_argc = margc;
551 cursor_argo = strlen(margv[margc-1]);
552 }
553 #endif /* !SMALL */
554 }
555
556 #ifdef SMALL
557 #define INC_CHKCURSOR(x) (x)++
558 #else /* !SMALL */
559 #define INC_CHKCURSOR(x) { (x)++ ; \
560 if (x == cursor_pos) { \
561 cursor_argc = margc; \
562 cursor_argo = ap-argbase; \
563 cursor_pos = NULL; \
564 } }
565
566 #endif /* !SMALL */
567
568 /*
569 * Parse string into argbuf;
570 * implemented with FSM to
571 * handle quoting and strings
572 */
573 char *
574 slurpstring()
575 {
576 int got_one = 0;
577 char *sb = stringbase;
578 char *ap = argbase;
579 char *tmp = argbase; /* will return this if token found */
580
581 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
582 switch (slrflag) { /* and $ as token for macro invoke */
583 case 0:
584 slrflag++;
585 INC_CHKCURSOR(stringbase);
586 return ((*sb == '!') ? "!" : "$");
587 /* NOTREACHED */
588 case 1:
589 slrflag++;
590 altarg = stringbase;
591 break;
592 default:
593 break;
594 }
595 }
596
597 S0:
598 switch (*sb) {
599
600 case '\0':
601 goto OUT;
602
603 case ' ':
604 case '\t':
605 INC_CHKCURSOR(sb);
606 goto S0;
607
608 default:
609 switch (slrflag) {
610 case 0:
611 slrflag++;
612 break;
613 case 1:
614 slrflag++;
615 altarg = sb;
616 break;
617 default:
618 break;
619 }
620 goto S1;
621 }
622
623 S1:
624 switch (*sb) {
625
626 case ' ':
627 case '\t':
628 case '\0':
629 goto OUT; /* end of token */
630
631 case '\\':
632 INC_CHKCURSOR(sb);
633 goto S2; /* slurp next character */
634
635 case '"':
636 INC_CHKCURSOR(sb);
637 goto S3; /* slurp quoted string */
638
639 default:
640 *ap = *sb; /* add character to token */
641 ap++;
642 INC_CHKCURSOR(sb);
643 got_one = 1;
644 goto S1;
645 }
646
647 S2:
648 switch (*sb) {
649
650 case '\0':
651 goto OUT;
652
653 default:
654 *ap = *sb;
655 ap++;
656 INC_CHKCURSOR(sb);
657 got_one = 1;
658 goto S1;
659 }
660
661 S3:
662 switch (*sb) {
663
664 case '\0':
665 goto OUT;
666
667 case '"':
668 INC_CHKCURSOR(sb);
669 goto S1;
670
671 default:
672 *ap = *sb;
673 ap++;
674 INC_CHKCURSOR(sb);
675 got_one = 1;
676 goto S3;
677 }
678
679 OUT:
680 if (got_one)
681 *ap++ = '\0';
682 argbase = ap; /* update storage pointer */
683 stringbase = sb; /* update scan pointer */
684 if (got_one) {
685 return (tmp);
686 }
687 switch (slrflag) {
688 case 0:
689 slrflag++;
690 break;
691 case 1:
692 slrflag++;
693 altarg = NULL;
694 break;
695 default:
696 break;
697 }
698 return (NULL);
699 }
700
701 /*
702 * Help command.
703 * Call each command handler with argc == 0 and argv[0] == name.
704 */
705 void
706 help(argc, argv)
707 int argc;
708 char *argv[];
709 {
710 struct cmd *c;
711
712 if (argc == 1) {
713 StringList *buf;
714
715 buf = sl_init();
716 fprintf(ttyout,
717 "%sommands may be abbreviated. Commands are:\n\n",
718 proxy ? "Proxy c" : "C");
719 for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
720 if (c->c_name && (!proxy || c->c_proxy))
721 sl_add(buf, c->c_name);
722 list_vertical(buf);
723 sl_free(buf, 0);
724 return;
725 }
726
727 #define HELPINDENT ((int) sizeof("disconnect"))
728
729 while (--argc > 0) {
730 char *arg;
731
732 arg = *++argv;
733 c = getcmd(arg);
734 if (c == (struct cmd *)-1)
735 fprintf(ttyout, "?Ambiguous help command %s\n", arg);
736 else if (c == NULL)
737 fprintf(ttyout, "?Invalid help command %s\n", arg);
738 else
739 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
740 c->c_name, c->c_help);
741 }
742 }
743
744 void
745 usage()
746 {
747 (void)fprintf(stderr,
748 "usage: %s [-AadeginptvV] [-r retry] [-P port] [host [port]]\n"
749 " %s [-f] [-o outfile] file:///file\n"
750 " %s [-f] [-o outfile] ftp://[user[:pass]@]host[:port]/path[/]\n"
751 " %s [-f] [-o outfile] http://host[:port]/path\n"
752 " %s [-f] [-o outfile] host:path[/]\n",
753 __progname, __progname, __progname, __progname, __progname);
754 exit(1);
755 }
756