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