1 /* $OpenBSD: small.c,v 1.6 2016/05/25 15:36:01 krw Exp $ */ 2 /* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */ 3 4 /* 5 * Copyright (C) 1997 and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1985, 1989, 1993, 1994 35 * The Regents of the University of California. All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 /* 63 * FTP User Program -- Command Routines. 64 */ 65 #include <sys/types.h> 66 #include <sys/socket.h> 67 #include <sys/stat.h> 68 #include <sys/wait.h> 69 #include <arpa/ftp.h> 70 71 #include <ctype.h> 72 #include <err.h> 73 #include <fnmatch.h> 74 #include <glob.h> 75 #include <netdb.h> 76 #include <stdio.h> 77 #include <stdlib.h> 78 #include <string.h> 79 #include <unistd.h> 80 #include <errno.h> 81 82 #include "ftp_var.h" 83 #include "pathnames.h" 84 #include "small.h" 85 86 jmp_buf jabort; 87 char *mname; 88 char *home = "/"; 89 90 struct types { 91 char *t_name; 92 char *t_mode; 93 int t_type; 94 } types[] = { 95 { "ascii", "A", TYPE_A }, 96 { "binary", "I", TYPE_I }, 97 { "image", "I", TYPE_I }, 98 { NULL } 99 }; 100 101 /* 102 * Set transfer type. 103 */ 104 void 105 settype(int argc, char *argv[]) 106 { 107 struct types *p; 108 int comret; 109 110 if (argc > 2) { 111 char *sep; 112 113 fprintf(ttyout, "usage: %s [", argv[0]); 114 sep = ""; 115 for (p = types; p->t_name; p++) { 116 fprintf(ttyout, "%s%s", sep, p->t_name); 117 sep = " | "; 118 } 119 fputs("]\n", ttyout); 120 code = -1; 121 return; 122 } 123 if (argc < 2) { 124 fprintf(ttyout, "Using %s mode to transfer files.\n", typename); 125 code = 0; 126 return; 127 } 128 for (p = types; p->t_name; p++) 129 if (strcmp(argv[1], p->t_name) == 0) 130 break; 131 if (p->t_name == 0) { 132 fprintf(ttyout, "%s: unknown mode.\n", argv[1]); 133 code = -1; 134 return; 135 } 136 comret = command("TYPE %s", p->t_mode); 137 if (comret == COMPLETE) { 138 (void)strlcpy(typename, p->t_name, sizeof typename); 139 curtype = type = p->t_type; 140 } 141 } 142 143 /* 144 * Internal form of settype; changes current type in use with server 145 * without changing our notion of the type for data transfers. 146 * Used to change to and from ascii for listings. 147 */ 148 void 149 changetype(int newtype, int show) 150 { 151 struct types *p; 152 int comret, oldverbose = verbose; 153 154 if (newtype == 0) 155 newtype = TYPE_I; 156 if (newtype == curtype) 157 return; 158 if ( 159 #ifndef SMALL 160 !debug && 161 #endif /* !SMALL */ 162 show == 0) 163 verbose = 0; 164 for (p = types; p->t_name; p++) 165 if (newtype == p->t_type) 166 break; 167 if (p->t_name == 0) { 168 warnx("internal error: unknown type %d.", newtype); 169 return; 170 } 171 if (newtype == TYPE_L && bytename[0] != '\0') 172 comret = command("TYPE %s %s", p->t_mode, bytename); 173 else 174 comret = command("TYPE %s", p->t_mode); 175 if (comret == COMPLETE) 176 curtype = newtype; 177 verbose = oldverbose; 178 } 179 180 char *stype[] = { 181 "type", 182 "", 183 0 184 }; 185 186 /* 187 * Set binary transfer type. 188 */ 189 /*ARGSUSED*/ 190 void 191 setbinary(int argc, char *argv[]) 192 { 193 194 stype[1] = "binary"; 195 settype(2, stype); 196 } 197 198 void 199 get(int argc, char *argv[]) 200 { 201 202 (void)getit(argc, argv, 0, restart_point ? "a+w" : "w" ); 203 } 204 205 /* 206 * Receive one file. 207 */ 208 int 209 getit(int argc, char *argv[], int restartit, const char *mode) 210 { 211 int loc = 0; 212 int rval = 0; 213 char *oldargv1, *oldargv2, *globargv2; 214 215 if (argc == 2) { 216 argc++; 217 argv[2] = argv[1]; 218 loc++; 219 } 220 #ifndef SMALL 221 if (argc < 2 && !another(&argc, &argv, "remote-file")) 222 goto usage; 223 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { 224 usage: 225 fprintf(ttyout, "usage: %s remote-file [local-file]\n", 226 argv[0]); 227 code = -1; 228 return (0); 229 } 230 #endif /* !SMALL */ 231 oldargv1 = argv[1]; 232 oldargv2 = argv[2]; 233 if (!globulize(&argv[2])) { 234 code = -1; 235 return (0); 236 } 237 globargv2 = argv[2]; 238 if (loc && mcase) { 239 char *tp = argv[1], *tp2, tmpbuf[PATH_MAX]; 240 241 while (*tp && !islower((unsigned char)*tp)) { 242 tp++; 243 } 244 if (!*tp) { 245 tp = argv[2]; 246 tp2 = tmpbuf; 247 while ((*tp2 = *tp) != '\0') { 248 if (isupper((unsigned char)*tp2)) { 249 *tp2 = tolower((unsigned char)*tp2); 250 } 251 tp++; 252 tp2++; 253 } 254 argv[2] = tmpbuf; 255 } 256 } 257 if (loc && ntflag) 258 argv[2] = dotrans(argv[2]); 259 if (loc && mapflag) 260 argv[2] = domap(argv[2]); 261 #ifndef SMALL 262 if (restartit) { 263 struct stat stbuf; 264 int ret; 265 266 ret = stat(argv[2], &stbuf); 267 if (restartit == 1) { 268 restart_point = (ret < 0) ? 0 : stbuf.st_size; 269 } else { 270 if (ret == 0) { 271 time_t mtime; 272 273 mtime = remotemodtime(argv[1], 0); 274 if (mtime == -1) 275 goto freegetit; 276 if (stbuf.st_mtime >= mtime) { 277 rval = 1; 278 fprintf(ttyout, 279 "Local file \"%s\" is newer "\ 280 "than remote file \"%s\".\n", 281 argv[2], argv[1]); 282 goto freegetit; 283 } 284 } 285 } 286 } 287 #endif /* !SMALL */ 288 289 recvrequest("RETR", argv[2], argv[1], mode, 290 argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc); 291 restart_point = 0; 292 freegetit: 293 if (oldargv2 != globargv2) /* free up after globulize() */ 294 free(globargv2); 295 return (rval); 296 } 297 298 /* XXX - Signal race. */ 299 /* ARGSUSED */ 300 void 301 mabort(int signo) 302 { 303 int save_errno = errno; 304 305 alarmtimer(0); 306 (void) write(fileno(ttyout), "\n\r", 2); 307 #ifndef SMALL 308 if (mflag && fromatty) { 309 /* XXX signal race, crazy unbelievable stdio misuse */ 310 if (confirm(mname, NULL)) { 311 errno = save_errno; 312 longjmp(jabort, 1); 313 } 314 } 315 #endif /* !SMALL */ 316 mflag = 0; 317 errno = save_errno; 318 longjmp(jabort, 1); 319 } 320 321 /* 322 * Get multiple files. 323 */ 324 void 325 mget(int argc, char *argv[]) 326 { 327 extern int optind, optreset; 328 sig_t oldintr; 329 int ch, xargc = 2; 330 char *cp, localcwd[PATH_MAX], *xargv[] = { argv[0], NULL, NULL }; 331 static int restartit = 0; 332 #ifndef SMALL 333 extern char *optarg; 334 const char *errstr; 335 int i = 1; 336 char type = 0, *dummyargv[] = { argv[0], ".", NULL }; 337 FILE *ftemp = NULL; 338 static int depth = 0, max_depth = 0; 339 340 optind = optreset = 1; 341 342 if (depth) 343 depth++; 344 345 while ((ch = getopt(argc, argv, "cd:nr")) != -1) { 346 switch(ch) { 347 case 'c': 348 restartit = 1; 349 break; 350 case 'd': 351 max_depth = strtonum(optarg, 0, INT_MAX, &errstr); 352 if (errstr != NULL) { 353 fprintf(ttyout, "bad depth value, %s: %s\n", 354 errstr, optarg); 355 code = -1; 356 return; 357 } 358 break; 359 case 'n': 360 restartit = -1; 361 break; 362 case 'r': 363 depth = 1; 364 break; 365 default: 366 goto usage; 367 } 368 } 369 370 if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) { 371 usage: 372 fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n", 373 argv[0]); 374 code = -1; 375 return; 376 } 377 378 argv[optind - 1] = argv[0]; 379 argc -= optind - 1; 380 argv += optind - 1; 381 #endif /* !SMALL */ 382 383 mname = argv[0]; 384 mflag = 1; 385 if (getcwd(localcwd, sizeof(localcwd)) == NULL) 386 err(1, "can't get cwd"); 387 388 oldintr = signal(SIGINT, mabort); 389 (void)setjmp(jabort); 390 while ((cp = 391 #ifdef SMALL 392 remglob(argv, proxy, NULL)) != NULL 393 ) { 394 #else /* SMALL */ 395 depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) : 396 remglob(argv, proxy, NULL)) != NULL 397 || (mflag && depth && ++i < argc) 398 ) { 399 if (cp == NULL) 400 continue; 401 #endif /* SMALL */ 402 if (*cp == '\0') { 403 mflag = 0; 404 continue; 405 } 406 if (!mflag) 407 continue; 408 #ifndef SMALL 409 if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0) 410 continue; 411 #endif /* !SMALL */ 412 if (!fileindir(cp, localcwd)) { 413 fprintf(ttyout, "Skipping non-relative filename `%s'\n", 414 cp); 415 continue; 416 } 417 #ifndef SMALL 418 if (type == 'd' && depth == max_depth) 419 continue; 420 if (!confirm(argv[0], cp)) 421 continue; 422 if (type == 'd') { 423 mkdir(cp, 0755); 424 if (chdir(cp) != 0) { 425 warn("local: %s", cp); 426 continue; 427 } 428 429 xargv[1] = cp; 430 cd(xargc, xargv); 431 if (dirchange != 1) 432 goto out; 433 434 xargv[1] = "*"; 435 mget(xargc, xargv); 436 437 xargv[1] = ".."; 438 cd(xargc, xargv); 439 if (dirchange != 1) { 440 mflag = 0; 441 goto out; 442 } 443 444 out: 445 if (chdir("..") != 0) { 446 warn("local: %s", cp); 447 mflag = 0; 448 } 449 continue; 450 } 451 if (type == 's') 452 /* Currently ignored. */ 453 continue; 454 #endif /* !SMALL */ 455 xargv[1] = cp; 456 (void)getit(xargc, xargv, restartit, 457 (restartit == 1 || restart_point) ? "a+w" : "w"); 458 #ifndef SMALL 459 if (!mflag && fromatty) { 460 if (confirm(argv[0], NULL)) 461 mflag = 1; 462 } 463 #endif /* !SMALL */ 464 } 465 (void)signal(SIGINT, oldintr); 466 #ifndef SMALL 467 if (depth) 468 depth--; 469 if (depth == 0 || mflag == 0) 470 depth = max_depth = mflag = restartit = 0; 471 #else /* !SMALL */ 472 mflag = 0; 473 #endif /* !SMALL */ 474 } 475 476 /* 477 * Set current working directory on remote machine. 478 */ 479 void 480 cd(int argc, char *argv[]) 481 { 482 int r; 483 484 #ifndef SMALL 485 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) || 486 argc > 2) { 487 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); 488 code = -1; 489 return; 490 } 491 #endif /* !SMALL */ 492 r = command("CWD %s", argv[1]); 493 if (r == ERROR && code == 500) { 494 if (verbose) 495 fputs("CWD command not recognized, trying XCWD.\n", ttyout); 496 r = command("XCWD %s", argv[1]); 497 } 498 if (r == ERROR && code == 550) { 499 dirchange = 0; 500 return; 501 } 502 if (r == COMPLETE) 503 dirchange = 1; 504 } 505 506 /* 507 * Terminate session, but don't exit. 508 */ 509 /* ARGSUSED */ 510 void 511 disconnect(int argc, char *argv[]) 512 { 513 514 if (!connected) 515 return; 516 (void)command("QUIT"); 517 if (cout) { 518 (void)fclose(cout); 519 } 520 cout = NULL; 521 connected = 0; 522 data = -1; 523 #ifndef SMALL 524 if (!proxy) { 525 macnum = 0; 526 } 527 #endif /* !SMALL */ 528 } 529 530 char * 531 dotrans(char *name) 532 { 533 static char new[PATH_MAX]; 534 char *cp1, *cp2 = new; 535 int i, ostop, found; 536 537 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 538 continue; 539 for (cp1 = name; *cp1; cp1++) { 540 found = 0; 541 for (i = 0; *(ntin + i) && i < 16; i++) { 542 if (*cp1 == *(ntin + i)) { 543 found++; 544 if (i < ostop) { 545 *cp2++ = *(ntout + i); 546 } 547 break; 548 } 549 } 550 if (!found) { 551 *cp2++ = *cp1; 552 } 553 } 554 *cp2 = '\0'; 555 return (new); 556 } 557 558 char * 559 domap(char *name) 560 { 561 static char new[PATH_MAX]; 562 char *cp1 = name, *cp2 = mapin; 563 char *tp[9], *te[9]; 564 int i, toks[9], toknum = 0, match = 1; 565 566 for (i=0; i < 9; ++i) { 567 toks[i] = 0; 568 } 569 while (match && *cp1 && *cp2) { 570 switch (*cp2) { 571 case '\\': 572 if (*++cp2 != *cp1) { 573 match = 0; 574 } 575 break; 576 case '$': 577 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 578 if (*cp1 != *(++cp2+1)) { 579 toks[toknum = *cp2 - '1']++; 580 tp[toknum] = cp1; 581 while (*++cp1 && *(cp2+1) 582 != *cp1); 583 te[toknum] = cp1; 584 } 585 cp2++; 586 break; 587 } 588 /* FALLTHROUGH */ 589 default: 590 if (*cp2 != *cp1) { 591 match = 0; 592 } 593 break; 594 } 595 if (match && *cp1) { 596 cp1++; 597 } 598 if (match && *cp2) { 599 cp2++; 600 } 601 } 602 if (!match && *cp1) /* last token mismatch */ 603 { 604 toks[toknum] = 0; 605 } 606 cp1 = new; 607 *cp1 = '\0'; 608 cp2 = mapout; 609 while (*cp2) { 610 match = 0; 611 switch (*cp2) { 612 case '\\': 613 if (*(cp2 + 1)) { 614 *cp1++ = *++cp2; 615 } 616 break; 617 case '[': 618 LOOP: 619 if (*++cp2 == '$' && isdigit((unsigned char)*(cp2 + 1))) { 620 if (*++cp2 == '0') { 621 char *cp3 = name; 622 623 while (*cp3) { 624 *cp1++ = *cp3++; 625 } 626 match = 1; 627 } 628 else if (toks[toknum = *cp2 - '1']) { 629 char *cp3 = tp[toknum]; 630 631 while (cp3 != te[toknum]) { 632 *cp1++ = *cp3++; 633 } 634 match = 1; 635 } 636 } 637 else { 638 while (*cp2 && *cp2 != ',' && 639 *cp2 != ']') { 640 if (*cp2 == '\\') { 641 cp2++; 642 } 643 else if (*cp2 == '$' && 644 isdigit((unsigned char)*(cp2 + 1))) { 645 if (*++cp2 == '0') { 646 char *cp3 = name; 647 648 while (*cp3) { 649 *cp1++ = *cp3++; 650 } 651 } 652 else if (toks[toknum = 653 *cp2 - '1']) { 654 char *cp3=tp[toknum]; 655 656 while (cp3 != 657 te[toknum]) { 658 *cp1++ = *cp3++; 659 } 660 } 661 } 662 else if (*cp2) { 663 *cp1++ = *cp2++; 664 } 665 } 666 if (!*cp2) { 667 fputs( 668 "nmap: unbalanced brackets.\n", ttyout); 669 return (name); 670 } 671 match = 1; 672 cp2--; 673 } 674 if (match) { 675 while (*++cp2 && *cp2 != ']') { 676 if (*cp2 == '\\' && *(cp2 + 1)) { 677 cp2++; 678 } 679 } 680 if (!*cp2) { 681 fputs( 682 "nmap: unbalanced brackets.\n", ttyout); 683 return (name); 684 } 685 break; 686 } 687 switch (*++cp2) { 688 case ',': 689 goto LOOP; 690 case ']': 691 break; 692 default: 693 cp2--; 694 goto LOOP; 695 } 696 break; 697 case '$': 698 if (isdigit((unsigned char)*(cp2 + 1))) { 699 if (*++cp2 == '0') { 700 char *cp3 = name; 701 702 while (*cp3) { 703 *cp1++ = *cp3++; 704 } 705 } 706 else if (toks[toknum = *cp2 - '1']) { 707 char *cp3 = tp[toknum]; 708 709 while (cp3 != te[toknum]) { 710 *cp1++ = *cp3++; 711 } 712 } 713 break; 714 } 715 /* FALLTHROUGH */ 716 default: 717 *cp1++ = *cp2; 718 break; 719 } 720 cp2++; 721 } 722 *cp1 = '\0'; 723 if (!*new) { 724 return (name); 725 } 726 return (new); 727 } 728 729