1 /* $OpenBSD: small.c,v 1.13 2023/03/08 04:43:11 guenther 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 void 190 setbinary(int argc, char *argv[]) 191 { 192 193 stype[1] = "binary"; 194 settype(2, stype); 195 } 196 197 void 198 get(int argc, char *argv[]) 199 { 200 201 (void)getit(argc, argv, 0, restart_point ? "a+w" : "w" ); 202 } 203 204 /* 205 * Receive one file. 206 */ 207 int 208 getit(int argc, char *argv[], int restartit, const char *mode) 209 { 210 int loc = 0; 211 int rval = 0; 212 char *oldargv1, *oldargv2, *globargv2; 213 214 if (argc == 2) { 215 argc++; 216 argv[2] = argv[1]; 217 loc++; 218 } 219 #ifndef SMALL 220 if (argc < 2 && !another(&argc, &argv, "remote-file")) 221 goto usage; 222 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { 223 usage: 224 fprintf(ttyout, "usage: %s remote-file [local-file]\n", 225 argv[0]); 226 code = -1; 227 return (0); 228 } 229 #endif /* !SMALL */ 230 oldargv1 = argv[1]; 231 oldargv2 = argv[2]; 232 if (!globulize(&argv[2])) { 233 code = -1; 234 return (0); 235 } 236 globargv2 = argv[2]; 237 if (loc && mcase) { 238 char *tp = argv[1], *tp2, tmpbuf[PATH_MAX]; 239 240 while (*tp && !islower((unsigned char)*tp)) { 241 tp++; 242 } 243 if (!*tp) { 244 tp = argv[2]; 245 tp2 = tmpbuf; 246 while ((*tp2 = *tp) != '\0') { 247 if (isupper((unsigned char)*tp2)) { 248 *tp2 = tolower((unsigned char)*tp2); 249 } 250 tp++; 251 tp2++; 252 } 253 argv[2] = tmpbuf; 254 } 255 } 256 if (loc && ntflag) 257 argv[2] = dotrans(argv[2]); 258 if (loc && mapflag) 259 argv[2] = domap(argv[2]); 260 #ifndef SMALL 261 if (restartit) { 262 struct stat stbuf; 263 int ret; 264 265 ret = stat(argv[2], &stbuf); 266 if (restartit == 1) { 267 restart_point = (ret < 0) ? 0 : stbuf.st_size; 268 } else { 269 if (ret == 0) { 270 time_t mtime; 271 272 mtime = remotemodtime(argv[1], 0); 273 if (mtime == -1) 274 goto freegetit; 275 if (stbuf.st_mtime >= mtime) { 276 rval = 1; 277 fprintf(ttyout, 278 "Local file \"%s\" is newer "\ 279 "than remote file \"%s\".\n", 280 argv[2], argv[1]); 281 goto freegetit; 282 } 283 } 284 } 285 } 286 #endif /* !SMALL */ 287 288 recvrequest("RETR", argv[2], argv[1], mode, 289 argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc); 290 restart_point = 0; 291 #ifndef SMALL 292 freegetit: 293 #endif 294 if (oldargv2 != globargv2) /* free up after globulize() */ 295 free(globargv2); 296 return (rval); 297 } 298 299 /* XXX - Signal race. */ 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 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 ch, 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 void 510 disconnect(int argc, char *argv[]) 511 { 512 513 if (!connected) 514 return; 515 (void)command("QUIT"); 516 if (cout) { 517 (void)fclose(cout); 518 } 519 cout = NULL; 520 connected = 0; 521 data = -1; 522 #ifndef SMALL 523 if (!proxy) { 524 macnum = 0; 525 } 526 #endif /* !SMALL */ 527 } 528 529 char * 530 dotrans(char *name) 531 { 532 static char new[PATH_MAX]; 533 char *cp1, *cp2 = new; 534 int i, ostop, found; 535 536 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 537 continue; 538 for (cp1 = name; *cp1; cp1++) { 539 found = 0; 540 for (i = 0; *(ntin + i) && i < 16; i++) { 541 if (*cp1 == *(ntin + i)) { 542 found++; 543 if (i < ostop) { 544 *cp2++ = *(ntout + i); 545 } 546 break; 547 } 548 } 549 if (!found) { 550 *cp2++ = *cp1; 551 } 552 } 553 *cp2 = '\0'; 554 return (new); 555 } 556 557 char * 558 domap(char *name) 559 { 560 static char new[PATH_MAX]; 561 char *cp1 = name, *cp2 = mapin; 562 char *tp[9], *te[9]; 563 int i, toks[9], toknum = 0, match = 1; 564 565 for (i=0; i < 9; ++i) { 566 toks[i] = 0; 567 } 568 while (match && *cp1 && *cp2) { 569 switch (*cp2) { 570 case '\\': 571 if (*++cp2 != *cp1) { 572 match = 0; 573 } 574 break; 575 case '$': 576 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 577 if (*cp1 != *(++cp2+1)) { 578 toks[toknum = *cp2 - '1']++; 579 tp[toknum] = cp1; 580 while (*++cp1 && *(cp2+1) 581 != *cp1); 582 te[toknum] = cp1; 583 } 584 cp2++; 585 break; 586 } 587 /* FALLTHROUGH */ 588 default: 589 if (*cp2 != *cp1) { 590 match = 0; 591 } 592 break; 593 } 594 if (match && *cp1) { 595 cp1++; 596 } 597 if (match && *cp2) { 598 cp2++; 599 } 600 } 601 if (!match && *cp1) /* last token mismatch */ 602 { 603 toks[toknum] = 0; 604 } 605 cp1 = new; 606 *cp1 = '\0'; 607 cp2 = mapout; 608 while (*cp2) { 609 match = 0; 610 switch (*cp2) { 611 case '\\': 612 if (*(cp2 + 1)) { 613 *cp1++ = *++cp2; 614 } 615 break; 616 case '[': 617 LOOP: 618 if (*++cp2 == '$' && isdigit((unsigned char)*(cp2 + 1))) { 619 if (*++cp2 == '0') { 620 char *cp3 = name; 621 622 while (*cp3) { 623 *cp1++ = *cp3++; 624 } 625 match = 1; 626 } else if (toks[toknum = *cp2 - '1']) { 627 char *cp3 = tp[toknum]; 628 629 while (cp3 != te[toknum]) { 630 *cp1++ = *cp3++; 631 } 632 match = 1; 633 } 634 } else { 635 while (*cp2 && *cp2 != ',' && 636 *cp2 != ']') { 637 if (*cp2 == '\\') { 638 cp2++; 639 } else if (*cp2 == '$' && 640 isdigit((unsigned char)*(cp2 + 1))) { 641 if (*++cp2 == '0') { 642 char *cp3 = name; 643 644 while (*cp3) { 645 *cp1++ = *cp3++; 646 } 647 } else if (toks[toknum = 648 *cp2 - '1']) { 649 char *cp3=tp[toknum]; 650 651 while (cp3 != 652 te[toknum]) { 653 *cp1++ = *cp3++; 654 } 655 } 656 } else if (*cp2) { 657 *cp1++ = *cp2++; 658 } 659 } 660 if (!*cp2) { 661 fputs( 662 "nmap: unbalanced brackets.\n", ttyout); 663 return (name); 664 } 665 match = 1; 666 cp2--; 667 } 668 if (match) { 669 while (*++cp2 && *cp2 != ']') { 670 if (*cp2 == '\\' && *(cp2 + 1)) { 671 cp2++; 672 } 673 } 674 if (!*cp2) { 675 fputs( 676 "nmap: unbalanced brackets.\n", ttyout); 677 return (name); 678 } 679 break; 680 } 681 switch (*++cp2) { 682 case ',': 683 goto LOOP; 684 case ']': 685 break; 686 default: 687 cp2--; 688 goto LOOP; 689 } 690 break; 691 case '$': 692 if (isdigit((unsigned char)*(cp2 + 1))) { 693 if (*++cp2 == '0') { 694 char *cp3 = name; 695 696 while (*cp3) { 697 *cp1++ = *cp3++; 698 } 699 } else if (toks[toknum = *cp2 - '1']) { 700 char *cp3 = tp[toknum]; 701 702 while (cp3 != te[toknum]) { 703 *cp1++ = *cp3++; 704 } 705 } 706 break; 707 } 708 /* FALLTHROUGH */ 709 default: 710 *cp1++ = *cp2; 711 break; 712 } 713 cp2++; 714 } 715 *cp1 = '\0'; 716 if (!*new) { 717 return (name); 718 } 719 return (new); 720 } 721 722