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