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: main.c,v 1.33.2.1 2003/08/20 16:43:14 lha Exp $"); 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 "Use GSS-API bindings", NULL}, 65 #endif 66 { NULL, 'v', arg_counter, &verbose, 67 "verbosity", NULL}, 68 { NULL, 'K', arg_negative_flag, &use_kerberos, 69 "Disable kerberos authentication", NULL}, 70 { "version", 0, arg_flag, &version_flag }, 71 { "help", 'h', arg_flag, &help_flag }, 72 }; 73 74 static int num_args = sizeof(getargs) / sizeof(getargs[0]); 75 76 static void 77 usage(int ecode) 78 { 79 arg_printusage(getargs, num_args, NULL, "[host [port]]"); 80 exit(ecode); 81 } 82 83 int 84 main(int argc, char **argv) 85 { 86 int top; 87 struct passwd *pw = NULL; 88 char homedir[MaxPathLen]; 89 struct servent *sp; 90 int optind = 0; 91 92 setprogname(argv[0]); 93 94 sp = getservbyname("ftp", "tcp"); 95 if (sp == 0) 96 errx(1, "ftp/tcp: unknown service"); 97 doglob = 1; 98 interactive = 1; 99 autologin = 1; 100 lineedit = 1; 101 passivemode = 0; /* passive mode not active */ 102 use_kerberos = 1; 103 #ifdef KRB5 104 ftp_do_gss_bindings = 1; 105 #endif 106 107 if(getarg(getargs, num_args, argc, argv, &optind)) 108 usage(1); 109 if(help_flag) 110 usage(0); 111 if(version_flag) { 112 print_version(NULL); 113 exit(0); 114 } 115 116 if (debug_flag) { 117 options |= SO_DEBUG; 118 debug++; 119 } 120 121 argc -= optind; 122 argv += optind; 123 124 fromatty = isatty(fileno(stdin)); 125 if (fromatty) 126 verbose++; 127 cpend = 0; /* no pending replies */ 128 proxy = 0; /* proxy not active */ 129 crflag = 1; /* strip c.r. on ascii gets */ 130 sendport = -1; /* not using ports */ 131 /* 132 * Set up the home directory in case we're globbing. 133 */ 134 pw = k_getpwuid(getuid()); 135 if (pw != NULL) { 136 strlcpy(homedir, pw->pw_dir, sizeof(homedir)); 137 home = homedir; 138 } 139 if (argc > 0) { 140 char *xargv[5]; 141 142 if (setjmp(toplevel)) 143 exit(0); 144 signal(SIGINT, intr); 145 signal(SIGPIPE, lostpeer); 146 xargv[0] = (char*)getprogname(); 147 xargv[1] = argv[0]; 148 xargv[2] = argv[1]; 149 xargv[3] = argv[2]; 150 xargv[4] = NULL; 151 setpeer(argc+1, xargv); 152 } 153 if(setjmp(toplevel) == 0) 154 top = 1; 155 else 156 top = 0; 157 if (top) { 158 signal(SIGINT, intr); 159 signal(SIGPIPE, lostpeer); 160 } 161 for (;;) { 162 cmdscanner(top); 163 top = 1; 164 } 165 } 166 167 void 168 intr(int sig) 169 { 170 171 longjmp(toplevel, 1); 172 } 173 174 #ifndef SHUT_RDWR 175 #define SHUT_RDWR 2 176 #endif 177 178 RETSIGTYPE 179 lostpeer(int sig) 180 { 181 182 if (connected) { 183 if (cout != NULL) { 184 shutdown(fileno(cout), SHUT_RDWR); 185 fclose(cout); 186 cout = NULL; 187 } 188 if (data >= 0) { 189 shutdown(data, SHUT_RDWR); 190 close(data); 191 data = -1; 192 } 193 connected = 0; 194 } 195 pswitch(1); 196 if (connected) { 197 if (cout != NULL) { 198 shutdown(fileno(cout), SHUT_RDWR); 199 fclose(cout); 200 cout = NULL; 201 } 202 connected = 0; 203 } 204 proxflag = 0; 205 pswitch(0); 206 sec_end(); 207 SIGRETURN(0); 208 } 209 210 /* 211 char * 212 tail(filename) 213 char *filename; 214 { 215 char *s; 216 217 while (*filename) { 218 s = strrchr(filename, '/'); 219 if (s == NULL) 220 break; 221 if (s[1]) 222 return (s + 1); 223 *s = '\0'; 224 } 225 return (filename); 226 } 227 */ 228 229 static char * 230 simple_readline(char *prompt) 231 { 232 char buf[BUFSIZ]; 233 printf ("%s", prompt); 234 fflush (stdout); 235 if(fgets(buf, sizeof(buf), stdin) == NULL) 236 return NULL; 237 if (buf[strlen(buf) - 1] == '\n') 238 buf[strlen(buf) - 1] = '\0'; 239 return strdup(buf); 240 } 241 242 #ifndef HAVE_READLINE 243 244 static char * 245 readline(char *prompt) 246 { 247 return simple_readline (prompt); 248 } 249 250 static void 251 add_history(char *p) 252 { 253 } 254 255 #else 256 257 /* These should not really be here */ 258 259 char *readline(char *); 260 void add_history(char *); 261 262 #endif 263 264 /* 265 * Command parser. 266 */ 267 void 268 cmdscanner(int top) 269 { 270 struct cmd *c; 271 int l; 272 273 if (!top) 274 putchar('\n'); 275 for (;;) { 276 if (fromatty) { 277 char *p; 278 if (lineedit) 279 p = readline("ftp> "); 280 else 281 p = simple_readline("ftp> "); 282 if(p == NULL) { 283 printf("\n"); 284 quit(0, 0); 285 } 286 strlcpy(line, p, sizeof(line)); 287 if (lineedit) 288 add_history(p); 289 free(p); 290 } else{ 291 if (fgets(line, sizeof line, stdin) == NULL) 292 quit(0, 0); 293 } 294 /* XXX will break on long lines */ 295 l = strlen(line); 296 if (l == 0) 297 break; 298 if (line[--l] == '\n') { 299 if (l == 0) 300 break; 301 line[l] = '\0'; 302 } else if (l == sizeof(line) - 2) { 303 printf("sorry, input line too long\n"); 304 while ((l = getchar()) != '\n' && l != EOF) 305 /* void */; 306 break; 307 } /* else it was a line without a newline */ 308 makeargv(); 309 if (margc == 0) { 310 continue; 311 } 312 c = getcmd(margv[0]); 313 if (c == (struct cmd *)-1) { 314 printf("?Ambiguous command\n"); 315 continue; 316 } 317 if (c == 0) { 318 printf("?Invalid command\n"); 319 continue; 320 } 321 if (c->c_conn && !connected) { 322 printf("Not connected.\n"); 323 continue; 324 } 325 (*c->c_handler)(margc, margv); 326 if (bell && c->c_bell) 327 putchar('\007'); 328 if (c->c_handler != help) 329 break; 330 } 331 signal(SIGINT, intr); 332 signal(SIGPIPE, lostpeer); 333 } 334 335 struct cmd * 336 getcmd(char *name) 337 { 338 char *p, *q; 339 struct cmd *c, *found; 340 int nmatches, longest; 341 342 longest = 0; 343 nmatches = 0; 344 found = 0; 345 for (c = cmdtab; (p = c->c_name); c++) { 346 for (q = name; *q == *p++; q++) 347 if (*q == 0) /* exact match? */ 348 return (c); 349 if (!*q) { /* the name was a prefix */ 350 if (q - name > longest) { 351 longest = q - name; 352 nmatches = 1; 353 found = c; 354 } else if (q - name == longest) 355 nmatches++; 356 } 357 } 358 if (nmatches > 1) 359 return ((struct cmd *)-1); 360 return (found); 361 } 362 363 /* 364 * Slice a string up into argc/argv. 365 */ 366 367 int slrflag; 368 369 void 370 makeargv(void) 371 { 372 char **argp; 373 374 argp = margv; 375 stringbase = line; /* scan from first of buffer */ 376 argbase = argbuf; /* store from first of buffer */ 377 slrflag = 0; 378 for (margc = 0; ; margc++) { 379 /* Expand array if necessary */ 380 if (margc == margvlen) { 381 int i; 382 383 margv = (margvlen == 0) 384 ? (char **)malloc(20 * sizeof(char *)) 385 : (char **)realloc(margv, 386 (margvlen + 20)*sizeof(char *)); 387 if (margv == NULL) 388 errx(1, "cannot realloc argv array"); 389 for(i = margvlen; i < margvlen + 20; ++i) 390 margv[i] = NULL; 391 margvlen += 20; 392 argp = margv + margc; 393 } 394 395 if ((*argp++ = slurpstring()) == NULL) 396 break; 397 } 398 399 } 400 401 /* 402 * Parse string into argbuf; 403 * implemented with FSM to 404 * handle quoting and strings 405 */ 406 char * 407 slurpstring(void) 408 { 409 int got_one = 0; 410 char *sb = stringbase; 411 char *ap = argbase; 412 char *tmp = argbase; /* will return this if token found */ 413 414 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 415 switch (slrflag) { /* and $ as token for macro invoke */ 416 case 0: 417 slrflag++; 418 stringbase++; 419 return ((*sb == '!') ? "!" : "$"); 420 /* NOTREACHED */ 421 case 1: 422 slrflag++; 423 altarg = stringbase; 424 break; 425 default: 426 break; 427 } 428 } 429 430 S0: 431 switch (*sb) { 432 433 case '\0': 434 goto OUT; 435 436 case ' ': 437 case '\t': 438 sb++; goto S0; 439 440 default: 441 switch (slrflag) { 442 case 0: 443 slrflag++; 444 break; 445 case 1: 446 slrflag++; 447 altarg = sb; 448 break; 449 default: 450 break; 451 } 452 goto S1; 453 } 454 455 S1: 456 switch (*sb) { 457 458 case ' ': 459 case '\t': 460 case '\0': 461 goto OUT; /* end of token */ 462 463 case '\\': 464 sb++; goto S2; /* slurp next character */ 465 466 case '"': 467 sb++; goto S3; /* slurp quoted string */ 468 469 default: 470 *ap++ = *sb++; /* add character to token */ 471 got_one = 1; 472 goto S1; 473 } 474 475 S2: 476 switch (*sb) { 477 478 case '\0': 479 goto OUT; 480 481 default: 482 *ap++ = *sb++; 483 got_one = 1; 484 goto S1; 485 } 486 487 S3: 488 switch (*sb) { 489 490 case '\0': 491 goto OUT; 492 493 case '"': 494 sb++; goto S1; 495 496 default: 497 *ap++ = *sb++; 498 got_one = 1; 499 goto S3; 500 } 501 502 OUT: 503 if (got_one) 504 *ap++ = '\0'; 505 argbase = ap; /* update storage pointer */ 506 stringbase = sb; /* update scan pointer */ 507 if (got_one) { 508 return (tmp); 509 } 510 switch (slrflag) { 511 case 0: 512 slrflag++; 513 break; 514 case 1: 515 slrflag++; 516 altarg = (char *) 0; 517 break; 518 default: 519 break; 520 } 521 return NULL; 522 } 523 524 #define HELPINDENT ((int) sizeof ("directory")) 525 526 /* 527 * Help command. 528 * Call each command handler with argc == 0 and argv[0] == name. 529 */ 530 void 531 help(int argc, char **argv) 532 { 533 struct cmd *c; 534 535 if (argc == 1) { 536 int i, j, w, k; 537 int columns, width = 0, lines; 538 539 printf("Commands may be abbreviated. Commands are:\n\n"); 540 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 541 int len = strlen(c->c_name); 542 543 if (len > width) 544 width = len; 545 } 546 width = (width + 8) &~ 7; 547 columns = 80 / width; 548 if (columns == 0) 549 columns = 1; 550 lines = (NCMDS + columns - 1) / columns; 551 for (i = 0; i < lines; i++) { 552 for (j = 0; j < columns; j++) { 553 c = cmdtab + j * lines + i; 554 if (c->c_name && (!proxy || c->c_proxy)) { 555 printf("%s", c->c_name); 556 } 557 else if (c->c_name) { 558 for (k=0; k < strlen(c->c_name); k++) { 559 putchar(' '); 560 } 561 } 562 if (c + lines >= &cmdtab[NCMDS]) { 563 printf("\n"); 564 break; 565 } 566 w = strlen(c->c_name); 567 while (w < width) { 568 w = (w + 8) &~ 7; 569 putchar('\t'); 570 } 571 } 572 } 573 return; 574 } 575 while (--argc > 0) { 576 char *arg; 577 arg = *++argv; 578 c = getcmd(arg); 579 if (c == (struct cmd *)-1) 580 printf("?Ambiguous help command %s\n", arg); 581 else if (c == (struct cmd *)0) 582 printf("?Invalid help command %s\n", arg); 583 else 584 printf("%-*s\t%s\n", HELPINDENT, 585 c->c_name, c->c_help); 586 } 587 } 588