1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 #pragma ident "%Z%%M% %I% %E% SMI"
40
41 /*
42 * FTP User Program -- Command Interface.
43 */
44 #define EXTERN
45 #include "ftp_var.h"
46 #include <deflt.h> /* macros that make using libcmd easier */
47
48 static void usage(void);
49 static void timeout_sig(int sig);
50 static void cmdscanner(int top);
51 static void intr(int sig);
52 static char *slurpstring(void);
53 extern int use_eprt;
54
55 boolean_t ls_invokes_NLST = B_TRUE;
56
57 #include <gssapi/gssapi.h>
58 #include <gssapi/gssapi_ext.h>
59 #define GETOPT_STR "dginpstvET:axfm:"
60 #define USAGE_STR "[-adfginpstvx] [-m mech] [-T timeout] " \
61 "[hostname [port]]"
62
63 int
main(int argc,char * argv[])64 main(int argc, char *argv[])
65 {
66 char *cp;
67 int c, top;
68 struct passwd *pw = NULL;
69 char homedir[MAXPATHLEN];
70 char *temp_string = NULL;
71
72 (void) setlocale(LC_ALL, "");
73
74 buf = (char *)memalign(getpagesize(), FTPBUFSIZ);
75 if (buf == NULL) {
76 (void) fprintf(stderr, "ftp: memory allocation failed\n");
77 return (1);
78 }
79
80 timeoutms = timeout = 0;
81 doglob = 1;
82 interactive = 1;
83 autologin = 1;
84
85 autoauth = 0;
86 /* by default SYST command will be sent to determine system type */
87 skipsyst = 0;
88 fflag = 0;
89 autoencrypt = 0;
90 goteof = 0;
91 mechstr[0] = '\0';
92
93 sendport = -1; /* tri-state variable. start out in "automatic" mode. */
94 passivemode = 0;
95
96 while ((c = getopt(argc, argv, GETOPT_STR)) != EOF) {
97 switch (c) {
98 case 'd':
99 options |= SO_DEBUG;
100 debug++;
101 break;
102
103 case 'g':
104 doglob = 0;
105 break;
106
107 case 'i':
108 interactive = 0;
109 break;
110
111 case 'n':
112 autologin = 0;
113 break;
114
115 case 'p':
116 passivemode = 1;
117 break;
118
119 case 't':
120 trace++;
121 break;
122
123 case 'v':
124 verbose++;
125 break;
126
127 /* undocumented option: allows testing of EPRT */
128 case 'E':
129 use_eprt = 1;
130 break;
131
132 case 'T':
133 if (!isdigit(*optarg)) {
134 (void) fprintf(stderr,
135 "ftp: bad timeout: \"%s\"\n", optarg);
136 break;
137 }
138 timeout = atoi(optarg);
139 timeoutms = timeout * MILLISEC;
140 break;
141
142 case 'a':
143 autoauth = 1;
144 break;
145
146 case 'f':
147 autoauth = 1;
148 fflag = 1;
149 break;
150
151 case 'm':
152 autoauth = 1;
153 call(setmech, "ftp", optarg, 0);
154 if (code != 0)
155 exit(1);
156 break;
157
158 case 'x':
159 autoauth = 1;
160 autoencrypt = 1;
161 break;
162
163 case 's':
164 skipsyst = 1;
165 break;
166
167 case '?':
168 default:
169 usage();
170 }
171 }
172 argc -= optind;
173 argv += optind;
174
175 if (argc > 2)
176 usage();
177
178 fromatty = isatty(fileno(stdin));
179 /*
180 * Scan env, then DEFAULTFTPFILE
181 * for FTP_LS_SENDS_NLST
182 */
183 temp_string = getenv("FTP_LS_SENDS_NLST");
184 if (temp_string == NULL) { /* env var not set */
185 if (defopen(DEFAULTFTPFILE) == 0) {
186 /*
187 * turn off case sensitivity
188 */
189 int flags = defcntl(DC_GETFLAGS, 0);
190
191 TURNOFF(flags, DC_CASE);
192 (void) defcntl(DC_SETFLAGS, flags);
193
194 temp_string = defread("FTP_LS_SENDS_NLST=");
195 (void) defopen(NULL); /* close default file */
196 }
197 }
198 if (temp_string != NULL &&
199 strncasecmp(temp_string, "n", 1) == 0)
200 ls_invokes_NLST = B_FALSE;
201
202 /*
203 * Set up defaults for FTP.
204 */
205 (void) strcpy(typename, "ascii"), type = TYPE_A;
206 (void) strcpy(formname, "non-print"), form = FORM_N;
207 (void) strcpy(modename, "stream"), mode = MODE_S;
208 (void) strcpy(structname, "file"), stru = STRU_F;
209 (void) strcpy(bytename, "8"), bytesize = 8;
210 if (fromatty)
211 verbose++;
212 cpend = 0; /* no pending replies */
213 proxy = 0; /* proxy not active */
214 crflag = 1; /* strip c.r. on ascii gets */
215
216 if (mechstr[0] == '\0') {
217 strlcpy(mechstr, FTP_DEF_MECH, MECH_SZ);
218 }
219
220 /*
221 * Set up the home directory in case we're globbing.
222 */
223 cp = getlogin();
224 if (cp != NULL) {
225 pw = getpwnam(cp);
226 }
227 if (pw == NULL)
228 pw = getpwuid(getuid());
229 if (pw != NULL) {
230 home = homedir;
231 (void) strcpy(home, pw->pw_dir);
232 }
233 if (setjmp(timeralarm)) {
234 (void) fflush(stdout);
235 (void) printf("Connection timeout\n");
236 exit(1);
237 }
238 (void) signal(SIGALRM, timeout_sig);
239 reset_timer();
240 if (argc > 0) {
241 int nargc = 0;
242 char *nargv[4];
243
244 if (setjmp(toplevel))
245 return (0);
246 (void) signal(SIGINT, intr);
247 (void) signal(SIGPIPE, lostpeer);
248 nargv[nargc++] = "ftp";
249 nargv[nargc++] = argv[0]; /* hostname */
250 if (argc > 1)
251 nargv[nargc++] = argv[1]; /* port */
252 nargv[nargc] = NULL;
253 setpeer(nargc, nargv);
254 }
255 top = setjmp(toplevel) == 0;
256 if (top) {
257 (void) signal(SIGINT, intr);
258 (void) signal(SIGPIPE, lostpeer);
259 }
260
261 for (;;) {
262 cmdscanner(top);
263 top = 1;
264 }
265 }
266
267 static void
usage(void)268 usage(void)
269 {
270 (void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR);
271 exit(1);
272 }
273
274 void
reset_timer()275 reset_timer()
276 {
277 /* The test is just to reduce syscalls if timeouts aren't used */
278 if (timeout)
279 alarm(timeout);
280 }
281
282 void
stop_timer()283 stop_timer()
284 {
285 if (timeout)
286 alarm(0);
287 }
288
289 /*ARGSUSED*/
290 static void
timeout_sig(int sig)291 timeout_sig(int sig)
292 {
293 longjmp(timeralarm, 1);
294 }
295
296 /*ARGSUSED*/
297 static void
intr(int sig)298 intr(int sig)
299 {
300 longjmp(toplevel, 1);
301 }
302
303 /*ARGSUSED*/
304 void
lostpeer(int sig)305 lostpeer(int sig)
306 {
307 extern FILE *ctrl_out;
308 extern int data;
309
310 if (connected) {
311 if (ctrl_out != NULL) {
312 (void) shutdown(fileno(ctrl_out), 1+1);
313 (void) fclose(ctrl_out);
314 ctrl_out = NULL;
315 }
316 if (data >= 0) {
317 (void) shutdown(data, 1+1);
318 (void) close(data);
319 data = -1;
320 }
321 connected = 0;
322
323 auth_type = AUTHTYPE_NONE;
324 clevel = dlevel = PROT_C;
325 goteof = 0;
326 }
327 pswitch(1);
328 if (connected) {
329 if (ctrl_out != NULL) {
330 (void) shutdown(fileno(ctrl_out), 1+1);
331 (void) fclose(ctrl_out);
332 ctrl_out = NULL;
333 }
334 connected = 0;
335
336 auth_type = AUTHTYPE_NONE;
337 clevel = dlevel = PROT_C;
338 goteof = 0;
339 }
340 proxflag = 0;
341 pswitch(0);
342 }
343
344 /*
345 * Command parser.
346 */
347 static void
cmdscanner(int top)348 cmdscanner(int top)
349 {
350 struct cmd *c;
351
352 if (!top)
353 (void) putchar('\n');
354 for (;;) {
355 stop_timer();
356 if (fromatty) {
357 (void) printf("ftp> ");
358 (void) fflush(stdout);
359 }
360 if (fgets(line, sizeof (line), stdin) == 0) {
361 if (feof(stdin) || ferror(stdin))
362 quit(0, NULL);
363 break;
364 }
365 if (line[0] == 0)
366 break;
367 /* If not all, just discard rest of line */
368 if (line[strlen(line)-1] != '\n') {
369 while (fgetc(stdin) != '\n' && !feof(stdin) &&
370 !ferror(stdin))
371 ;
372 (void) printf("Line too long\n");
373 continue;
374 } else
375 line[strlen(line)-1] = 0;
376
377 makeargv();
378 if (margc == 0) {
379 continue;
380 }
381 c = getcmd(margv[0]);
382 if (c == (struct cmd *)-1) {
383 (void) printf("?Ambiguous command\n");
384 continue;
385 }
386 if (c == 0) {
387 (void) printf("?Invalid command\n");
388 continue;
389 }
390 if (c->c_conn && !connected) {
391 (void) printf("Not connected.\n");
392 continue;
393 }
394 reset_timer();
395 (*c->c_handler)(margc, margv);
396 #ifndef CTRL
397 #define CTRL(c) ((c)&037)
398 #endif
399 stop_timer();
400 if (bell && c->c_bell)
401 (void) putchar(CTRL('g'));
402 if (c->c_handler != help)
403 break;
404 }
405 (void) signal(SIGINT, intr);
406 (void) signal(SIGPIPE, lostpeer);
407 }
408
409 struct cmd *
getcmd(char * name)410 getcmd(char *name)
411 {
412 char *p, *q;
413 struct cmd *c, *found;
414 int nmatches, longest;
415 extern struct cmd cmdtab[];
416
417 if (name == NULL)
418 return (0);
419
420 longest = 0;
421 nmatches = 0;
422 found = 0;
423 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
424 for (q = name; *q == *p++; q++)
425 if (*q == 0) /* exact match? */
426 return (c);
427 if (!*q) { /* the name was a prefix */
428 if (q - name > longest) {
429 longest = q - name;
430 nmatches = 1;
431 found = c;
432 } else if (q - name == longest)
433 nmatches++;
434 }
435 }
436 if (nmatches > 1)
437 return ((struct cmd *)-1);
438 return (found);
439 }
440
441 /*
442 * Slice a string up into argc/argv.
443 */
444
445 static int slrflag;
446 #define MARGV_INC 20
447
448 void
makeargv(void)449 makeargv(void)
450 {
451 char **argp;
452 static int margv_size;
453
454 margc = 0;
455 stringbase = line; /* scan from first of buffer */
456 argbase = argbuf; /* store from first of buffer */
457 slrflag = 0;
458
459 if (!margv) {
460 margv_size = MARGV_INC;
461 if ((margv = malloc(margv_size * sizeof (char *))) == NULL)
462 fatal("Out of memory");
463 }
464 argp = margv;
465 while (*argp++ = slurpstring()) {
466 margc++;
467 if (margc == margv_size) {
468 margv_size += MARGV_INC;
469 if ((margv = realloc(margv,
470 margv_size * sizeof (char *))) == NULL)
471 fatal("Out of memory");
472 argp = margv + margc;
473 }
474 }
475 }
476
477 /*
478 * Parse string into argbuf;
479 * implemented with FSM to
480 * handle quoting and strings
481 */
482 static char *
slurpstring(void)483 slurpstring(void)
484 {
485 int got_one = 0;
486 char *sb = stringbase;
487 char *ap = argbase;
488 char *tmp = argbase; /* will return this if token found */
489 int len;
490
491 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
492 switch (slrflag) { /* and $ as token for macro invoke */
493 case 0:
494 slrflag++;
495 stringbase++;
496 return ((*sb == '!') ? "!" : "$");
497 case 1:
498 slrflag++;
499 altarg = stringbase;
500 break;
501 default:
502 break;
503 }
504 }
505
506 S0:
507 switch (*sb) {
508
509 case '\0':
510 goto OUT;
511
512 case ' ':
513 case '\t':
514 sb++; goto S0;
515
516 default:
517 switch (slrflag) {
518 case 0:
519 slrflag++;
520 break;
521 case 1:
522 slrflag++;
523 altarg = sb;
524 break;
525 default:
526 break;
527 }
528 goto S1;
529 }
530
531 S1:
532 switch (*sb) {
533
534 case ' ':
535 case '\t':
536 case '\0':
537 goto OUT; /* end of token */
538
539 case '\\':
540 sb++; goto S2; /* slurp next character */
541
542 case '"':
543 sb++; goto S3; /* slurp quoted string */
544
545 default:
546 if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
547 len = 1;
548 memcpy(ap, sb, len);
549 ap += len;
550 sb += len;
551 got_one = 1;
552 goto S1;
553 }
554
555 S2:
556 switch (*sb) {
557
558 case '\0':
559 goto OUT;
560
561 default:
562 if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
563 len = 1;
564 memcpy(ap, sb, len);
565 ap += len;
566 sb += len;
567 got_one = 1;
568 goto S1;
569 }
570
571 S3:
572 switch (*sb) {
573
574 case '\0':
575 goto OUT;
576
577 case '"':
578 sb++; goto S1;
579
580 default:
581 if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
582 len = 1;
583 memcpy(ap, sb, len);
584 ap += len;
585 sb += len;
586 got_one = 1;
587 goto S3;
588 }
589
590 OUT:
591 if (got_one)
592 *ap++ = '\0';
593 argbase = ap; /* update storage pointer */
594 stringbase = sb; /* update scan pointer */
595 if (got_one) {
596 return (tmp);
597 }
598 switch (slrflag) {
599 case 0:
600 slrflag++;
601 break;
602 case 1:
603 slrflag++;
604 altarg = (char *)0;
605 break;
606 default:
607 break;
608 }
609 return ((char *)0);
610 }
611
612 #define HELPINDENT (sizeof ("directory"))
613
614 /*
615 * Help command.
616 * Call each command handler with argc == 0 and argv[0] == name.
617 */
618 void
help(int argc,char * argv[])619 help(int argc, char *argv[])
620 {
621 struct cmd *c;
622 extern struct cmd cmdtab[];
623
624 if (argc == 1) {
625 int i, j, w, k;
626 int columns, width = 0, lines;
627 extern int NCMDS;
628
629 (void) printf(
630 "Commands may be abbreviated. Commands are:\n\n");
631 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
632 int len = strlen(c->c_name);
633
634 if (len > width)
635 width = len;
636 }
637 width = (width + 8) &~ 7;
638 columns = 80 / width;
639 if (columns == 0)
640 columns = 1;
641 lines = (NCMDS + columns - 1) / columns;
642 for (i = 0; i < lines; i++) {
643 for (j = 0; j < columns; j++) {
644 c = cmdtab + j * lines + i;
645 if (c->c_name && (!proxy || c->c_proxy)) {
646 (void) printf("%s", c->c_name);
647 } else if (c->c_name) {
648 for (k = 0; k < strlen(c->c_name);
649 k++) {
650 (void) putchar(' ');
651 }
652 }
653 if (c + lines >= &cmdtab[NCMDS]) {
654 (void) printf("\n");
655 break;
656 }
657 w = strlen(c->c_name);
658 while (w < width) {
659 w = (w + 8) &~ 7;
660 (void) putchar('\t');
661 }
662 }
663 }
664 return;
665 }
666 while (--argc > 0) {
667 char *arg;
668 arg = *++argv;
669 c = getcmd(arg);
670 if (c == (struct cmd *)-1)
671 (void) printf("?Ambiguous help command %s\n", arg);
672 else if (c == (struct cmd *)0)
673 (void) printf("?Invalid help command %s\n", arg);
674 else
675 (void) printf("%-*s\t%s\n", HELPINDENT,
676 c->c_name, c->c_help);
677 }
678 }
679
680 /*
681 * Call routine with argc, argv set from args (terminated by 0).
682 */
683 void
call(void (* routine)(int argc,char * argv[]),...)684 call(void (*routine)(int argc, char *argv[]), ...)
685 {
686 va_list ap;
687 char *argv[10];
688 int argc = 0;
689
690 va_start(ap, routine);
691 while ((argv[argc] = va_arg(ap, char *)) != (char *)0)
692 argc++;
693 va_end(ap);
694 (*routine)(argc, argv);
695 }
696