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 81 usage(int ecode) 82 { 83 arg_printusage(getargs, num_args, NULL, "[host [port]]"); 84 exit(ecode); 85 } 86 87 int 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 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 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 * 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 * 249 readline(char *prompt) 250 { 251 return simple_readline (prompt); 252 } 253 254 static void 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 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 * 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 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 * 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 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