1 /*
2 * Copyright (c) 1985, 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 /*
35 * FTP User Program -- Command Interface.
36 */
37
38 #include "ftp_locl.h"
39 #include <getarg.h>
40
41 RCSID("$Id$");
42
43 static int help_flag;
44 static int version_flag;
45 static int debug_flag;
46
47 struct getargs getargs[] = {
48 { NULL, 'd', arg_flag, &debug_flag,
49 "debug", NULL },
50 { NULL, 'g', arg_negative_flag, &doglob,
51 "disables globbing", NULL},
52 { NULL, 'i', arg_negative_flag, &interactive,
53 "Turn off interactive prompting", NULL},
54 { NULL, 'l', arg_negative_flag, &lineedit,
55 "Turn off line editing", NULL},
56 { NULL, 'n', arg_negative_flag, &autologin,
57 "Turn off auto-login", NULL},
58 { NULL, 'p', arg_flag, &passivemode,
59 "passive mode", NULL},
60 { NULL, 't', arg_counter, &trace,
61 "Packet tracing", NULL},
62 #ifdef KRB5
63 { "gss-bindings", 0, arg_negative_flag, &ftp_do_gss_bindings,
64 "Don't use GSS-API bindings", NULL},
65 { "gss-delegate", 0, arg_negative_flag, &ftp_do_gss_delegate,
66 "Disable delegation of GSS-API credentials", NULL},
67 #endif
68 { NULL, 'v', arg_counter, &verbose,
69 "verbosity", NULL},
70 { NULL, 'K', arg_negative_flag, &use_kerberos,
71 "Disable kerberos authentication", NULL},
72 { "encrypt", 'x', arg_flag, &doencrypt,
73 "Encrypt command and data channel if possible" },
74 { "version", 0, arg_flag, &version_flag },
75 { "help", 'h', arg_flag, &help_flag },
76 };
77
78 static int num_args = sizeof(getargs) / sizeof(getargs[0]);
79
80 static void
usage(int ecode)81 usage(int ecode)
82 {
83 arg_printusage(getargs, num_args, NULL, "[host [port]]");
84 exit(ecode);
85 }
86
87 int
main(int argc,char ** argv)88 main(int argc, char **argv)
89 {
90 int top;
91 struct passwd *pw = NULL;
92 char homedir[MaxPathLen];
93 struct servent *sp;
94 int optind = 0;
95
96 setprogname(argv[0]);
97
98 sp = getservbyname("ftp", "tcp");
99 if (sp == 0)
100 errx(1, "ftp/tcp: unknown service");
101 doglob = 1;
102 interactive = 1;
103 autologin = 1;
104 lineedit = 1;
105 passivemode = 0; /* passive mode not active */
106 use_kerberos = 1;
107 #ifdef KRB5
108 ftp_do_gss_bindings = 1;
109 #endif
110
111 if(getarg(getargs, num_args, argc, argv, &optind))
112 usage(1);
113 if(help_flag)
114 usage(0);
115 if(version_flag) {
116 print_version(NULL);
117 exit(0);
118 }
119
120 if (debug_flag) {
121 options |= SO_DEBUG;
122 debug++;
123 }
124
125 argc -= optind;
126 argv += optind;
127
128 fromatty = isatty(fileno(stdin));
129 if (fromatty)
130 verbose++;
131 cpend = 0; /* no pending replies */
132 proxy = 0; /* proxy not active */
133 crflag = 1; /* strip c.r. on ascii gets */
134 sendport = -1; /* not using ports */
135 /*
136 * Set up the home directory in case we're globbing.
137 */
138 pw = k_getpwuid(getuid());
139 if (pw != NULL) {
140 strlcpy(homedir, pw->pw_dir, sizeof(homedir));
141 home = homedir;
142 }
143 if (argc > 0) {
144 char *xargv[5];
145
146 if (setjmp(toplevel))
147 exit(0);
148 signal(SIGINT, intr);
149 signal(SIGPIPE, lostpeer);
150 xargv[0] = (char*)getprogname();
151 xargv[1] = argv[0];
152 xargv[2] = argv[1];
153 xargv[3] = argv[2];
154 xargv[4] = NULL;
155 setpeer(argc+1, xargv);
156 }
157 if(setjmp(toplevel) == 0)
158 top = 1;
159 else
160 top = 0;
161 if (top) {
162 signal(SIGINT, intr);
163 signal(SIGPIPE, lostpeer);
164 }
165 for (;;) {
166 cmdscanner(top);
167 top = 1;
168 }
169 }
170
171 void
intr(int sig)172 intr(int sig)
173 {
174
175 longjmp(toplevel, 1);
176 }
177
178 #ifndef SHUT_RDWR
179 #define SHUT_RDWR 2
180 #endif
181
182 RETSIGTYPE
lostpeer(int sig)183 lostpeer(int sig)
184 {
185
186 if (connected) {
187 if (cout != NULL) {
188 shutdown(fileno(cout), SHUT_RDWR);
189 fclose(cout);
190 cout = NULL;
191 }
192 if (data >= 0) {
193 shutdown(data, SHUT_RDWR);
194 close(data);
195 data = -1;
196 }
197 connected = 0;
198 }
199 pswitch(1);
200 if (connected) {
201 if (cout != NULL) {
202 shutdown(fileno(cout), SHUT_RDWR);
203 fclose(cout);
204 cout = NULL;
205 }
206 connected = 0;
207 }
208 proxflag = 0;
209 pswitch(0);
210 sec_end();
211 SIGRETURN(0);
212 }
213
214 /*
215 char *
216 tail(filename)
217 char *filename;
218 {
219 char *s;
220
221 while (*filename) {
222 s = strrchr(filename, '/');
223 if (s == NULL)
224 break;
225 if (s[1])
226 return (s + 1);
227 *s = '\0';
228 }
229 return (filename);
230 }
231 */
232
233 static char *
simple_readline(char * prompt)234 simple_readline(char *prompt)
235 {
236 char buf[BUFSIZ];
237 printf ("%s", prompt);
238 fflush (stdout);
239 if(fgets(buf, sizeof(buf), stdin) == NULL)
240 return NULL;
241 if (buf[strlen(buf) - 1] == '\n')
242 buf[strlen(buf) - 1] = '\0';
243 return strdup(buf);
244 }
245
246 #ifndef HAVE_READLINE
247
248 static char *
readline(char * prompt)249 readline(char *prompt)
250 {
251 return simple_readline (prompt);
252 }
253
254 static void
add_history(char * p)255 add_history(char *p)
256 {
257 }
258
259 #else
260
261 /* These should not really be here */
262
263 char *readline(char *);
264 void add_history(char *);
265
266 #endif
267
268 /*
269 * Command parser.
270 */
271 void
cmdscanner(int top)272 cmdscanner(int top)
273 {
274 struct cmd *c;
275 int l;
276
277 if (!top)
278 putchar('\n');
279 for (;;) {
280 if (fromatty) {
281 char *p;
282 if (lineedit)
283 p = readline("ftp> ");
284 else
285 p = simple_readline("ftp> ");
286 if(p == NULL) {
287 printf("\n");
288 quit(0, 0);
289 }
290 strlcpy(line, p, sizeof(line));
291 if (lineedit)
292 add_history(p);
293 free(p);
294 } else{
295 if (fgets(line, sizeof line, stdin) == NULL)
296 quit(0, 0);
297 }
298 /* XXX will break on long lines */
299 l = strlen(line);
300 if (l == 0)
301 break;
302 if (line[--l] == '\n') {
303 if (l == 0)
304 break;
305 line[l] = '\0';
306 } else if (l == sizeof(line) - 2) {
307 printf("sorry, input line too long\n");
308 while ((l = getchar()) != '\n' && l != EOF)
309 /* void */;
310 break;
311 } /* else it was a line without a newline */
312 makeargv();
313 if (margc == 0) {
314 continue;
315 }
316 c = getcmd(margv[0]);
317 if (c == (struct cmd *)-1) {
318 printf("?Ambiguous command\n");
319 continue;
320 }
321 if (c == 0) {
322 printf("?Invalid command\n");
323 continue;
324 }
325 if (c->c_conn && !connected) {
326 printf("Not connected.\n");
327 continue;
328 }
329 (*c->c_handler)(margc, margv);
330 if (bell && c->c_bell)
331 putchar('\007');
332 if (c->c_handler != help)
333 break;
334 }
335 signal(SIGINT, intr);
336 signal(SIGPIPE, lostpeer);
337 }
338
339 struct cmd *
getcmd(char * name)340 getcmd(char *name)
341 {
342 char *p, *q;
343 struct cmd *c, *found;
344 int nmatches, longest;
345
346 longest = 0;
347 nmatches = 0;
348 found = 0;
349 for (c = cmdtab; (p = c->c_name); c++) {
350 for (q = name; *q == *p++; q++)
351 if (*q == 0) /* exact match? */
352 return (c);
353 if (!*q) { /* the name was a prefix */
354 if (q - name > longest) {
355 longest = q - name;
356 nmatches = 1;
357 found = c;
358 } else if (q - name == longest)
359 nmatches++;
360 }
361 }
362 if (nmatches > 1)
363 return ((struct cmd *)-1);
364 return (found);
365 }
366
367 /*
368 * Slice a string up into argc/argv.
369 */
370
371 int slrflag;
372
373 void
makeargv(void)374 makeargv(void)
375 {
376 char **argp;
377
378 argp = margv;
379 stringbase = line; /* scan from first of buffer */
380 argbase = argbuf; /* store from first of buffer */
381 slrflag = 0;
382 for (margc = 0; ; margc++) {
383 /* Expand array if necessary */
384 if (margc == margvlen) {
385 int i;
386
387 margv = (margvlen == 0)
388 ? (char **)malloc(20 * sizeof(char *))
389 : (char **)realloc(margv,
390 (margvlen + 20)*sizeof(char *));
391 if (margv == NULL)
392 errx(1, "cannot realloc argv array");
393 for(i = margvlen; i < margvlen + 20; ++i)
394 margv[i] = NULL;
395 margvlen += 20;
396 argp = margv + margc;
397 }
398
399 if ((*argp++ = slurpstring()) == NULL)
400 break;
401 }
402
403 }
404
405 /*
406 * Parse string into argbuf;
407 * implemented with FSM to
408 * handle quoting and strings
409 */
410 char *
slurpstring(void)411 slurpstring(void)
412 {
413 int got_one = 0;
414 char *sb = stringbase;
415 char *ap = argbase;
416 char *tmp = argbase; /* will return this if token found */
417
418 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
419 switch (slrflag) { /* and $ as token for macro invoke */
420 case 0:
421 slrflag++;
422 stringbase++;
423 return ((*sb == '!') ? "!" : "$");
424 /* NOTREACHED */
425 case 1:
426 slrflag++;
427 altarg = stringbase;
428 break;
429 default:
430 break;
431 }
432 }
433
434 S0:
435 switch (*sb) {
436
437 case '\0':
438 goto OUT;
439
440 case ' ':
441 case '\t':
442 sb++; goto S0;
443
444 default:
445 switch (slrflag) {
446 case 0:
447 slrflag++;
448 break;
449 case 1:
450 slrflag++;
451 altarg = sb;
452 break;
453 default:
454 break;
455 }
456 goto S1;
457 }
458
459 S1:
460 switch (*sb) {
461
462 case ' ':
463 case '\t':
464 case '\0':
465 goto OUT; /* end of token */
466
467 case '\\':
468 sb++; goto S2; /* slurp next character */
469
470 case '"':
471 sb++; goto S3; /* slurp quoted string */
472
473 default:
474 *ap++ = *sb++; /* add character to token */
475 got_one = 1;
476 goto S1;
477 }
478
479 S2:
480 switch (*sb) {
481
482 case '\0':
483 goto OUT;
484
485 default:
486 *ap++ = *sb++;
487 got_one = 1;
488 goto S1;
489 }
490
491 S3:
492 switch (*sb) {
493
494 case '\0':
495 goto OUT;
496
497 case '"':
498 sb++; goto S1;
499
500 default:
501 *ap++ = *sb++;
502 got_one = 1;
503 goto S3;
504 }
505
506 OUT:
507 if (got_one)
508 *ap++ = '\0';
509 argbase = ap; /* update storage pointer */
510 stringbase = sb; /* update scan pointer */
511 if (got_one) {
512 return (tmp);
513 }
514 switch (slrflag) {
515 case 0:
516 slrflag++;
517 break;
518 case 1:
519 slrflag++;
520 altarg = (char *) 0;
521 break;
522 default:
523 break;
524 }
525 return NULL;
526 }
527
528 #define HELPINDENT ((int) sizeof ("directory"))
529
530 /*
531 * Help command.
532 * Call each command handler with argc == 0 and argv[0] == name.
533 */
534 void
help(int argc,char ** argv)535 help(int argc, char **argv)
536 {
537 struct cmd *c;
538
539 if (argc == 1) {
540 int i, j, w, k;
541 int columns, width = 0, lines;
542
543 printf("Commands may be abbreviated. Commands are:\n\n");
544 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
545 int len = strlen(c->c_name);
546
547 if (len > width)
548 width = len;
549 }
550 width = (width + 8) &~ 7;
551 columns = 80 / width;
552 if (columns == 0)
553 columns = 1;
554 lines = (NCMDS + columns - 1) / columns;
555 for (i = 0; i < lines; i++) {
556 for (j = 0; j < columns; j++) {
557 c = cmdtab + j * lines + i;
558 if ((!proxy || c->c_proxy)) {
559 printf("%s", c->c_name);
560 } else {
561 for (k=0; k < strlen(c->c_name); k++) {
562 putchar(' ');
563 }
564 }
565 if (c + lines >= &cmdtab[NCMDS]) {
566 printf("\n");
567 break;
568 }
569 w = strlen(c->c_name);
570 while (w < width) {
571 w = (w + 8) &~ 7;
572 putchar('\t');
573 }
574 }
575 }
576 return;
577 }
578 while (--argc > 0) {
579 char *arg;
580 arg = *++argv;
581 c = getcmd(arg);
582 if (c == (struct cmd *)-1)
583 printf("?Ambiguous help command %s\n", arg);
584 else if (c == (struct cmd *)0)
585 printf("?Invalid help command %s\n", arg);
586 else
587 printf("%-*s\t%s\n", HELPINDENT,
588 c->c_name, c->c_help);
589 }
590 }
591