1 /* $OpenBSD: cmds.c,v 1.66 2008/09/18 14:45:36 todd 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 #if !defined(lint) && !defined(SMALL) 63 static const char rcsid[] = "$OpenBSD: cmds.c,v 1.66 2008/09/18 14:45:36 todd Exp $"; 64 #endif /* not lint and not SMALL */ 65 66 /* 67 * FTP User Program -- Command Routines. 68 */ 69 #include <sys/types.h> 70 #include <sys/socket.h> 71 #include <sys/stat.h> 72 #include <sys/wait.h> 73 #include <arpa/ftp.h> 74 75 #include <ctype.h> 76 #include <err.h> 77 #ifndef SMALL 78 #include <fnmatch.h> 79 #endif /* !SMALL */ 80 #include <glob.h> 81 #include <netdb.h> 82 #include <stdio.h> 83 #include <stdlib.h> 84 #include <string.h> 85 #include <unistd.h> 86 87 #include "ftp_var.h" 88 #include "pathnames.h" 89 90 jmp_buf jabort; 91 char *mname; 92 char *home = "/"; 93 94 struct types { 95 char *t_name; 96 char *t_mode; 97 int t_type; 98 char *t_arg; 99 } types[] = { 100 { "ascii", "A", TYPE_A, 0 }, 101 { "binary", "I", TYPE_I, 0 }, 102 { "image", "I", TYPE_I, 0 }, 103 { "ebcdic", "E", TYPE_E, 0 }, 104 { "tenex", "L", TYPE_L, bytename }, 105 { NULL } 106 }; 107 108 /* 109 * Set transfer type. 110 */ 111 void 112 settype(int argc, char *argv[]) 113 { 114 struct types *p; 115 int comret; 116 117 if (argc > 2) { 118 char *sep; 119 120 fprintf(ttyout, "usage: %s [", argv[0]); 121 sep = ""; 122 for (p = types; p->t_name; p++) { 123 fprintf(ttyout, "%s%s", sep, p->t_name); 124 sep = " | "; 125 } 126 fputs("]\n", ttyout); 127 code = -1; 128 return; 129 } 130 if (argc < 2) { 131 fprintf(ttyout, "Using %s mode to transfer files.\n", typename); 132 code = 0; 133 return; 134 } 135 for (p = types; p->t_name; p++) 136 if (strcmp(argv[1], p->t_name) == 0) 137 break; 138 if (p->t_name == 0) { 139 fprintf(ttyout, "%s: unknown mode.\n", argv[1]); 140 code = -1; 141 return; 142 } 143 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) 144 comret = command("TYPE %s %s", p->t_mode, p->t_arg); 145 else 146 comret = command("TYPE %s", p->t_mode); 147 if (comret == COMPLETE) { 148 (void)strlcpy(typename, p->t_name, sizeof typename); 149 curtype = type = p->t_type; 150 } 151 } 152 153 /* 154 * Internal form of settype; changes current type in use with server 155 * without changing our notion of the type for data transfers. 156 * Used to change to and from ascii for listings. 157 */ 158 void 159 changetype(int newtype, int show) 160 { 161 struct types *p; 162 int comret, oldverbose = verbose; 163 164 if (newtype == 0) 165 newtype = TYPE_I; 166 if (newtype == curtype) 167 return; 168 if ( 169 #ifndef SMALL 170 !debug && 171 #endif /* !SMALL */ 172 show == 0) 173 verbose = 0; 174 for (p = types; p->t_name; p++) 175 if (newtype == p->t_type) 176 break; 177 if (p->t_name == 0) { 178 warnx("internal error: unknown type %d.", newtype); 179 return; 180 } 181 if (newtype == TYPE_L && bytename[0] != '\0') 182 comret = command("TYPE %s %s", p->t_mode, bytename); 183 else 184 comret = command("TYPE %s", p->t_mode); 185 if (comret == COMPLETE) 186 curtype = newtype; 187 verbose = oldverbose; 188 } 189 190 char *stype[] = { 191 "type", 192 "", 193 0 194 }; 195 196 /* 197 * Set binary transfer type. 198 */ 199 /*ARGSUSED*/ 200 void 201 setbinary(int argc, char *argv[]) 202 { 203 204 stype[1] = "binary"; 205 settype(2, stype); 206 } 207 208 /* 209 * Set ascii transfer type. 210 */ 211 /*ARGSUSED*/ 212 void 213 setascii(int argc, char *argv[]) 214 { 215 216 stype[1] = "ascii"; 217 settype(2, stype); 218 } 219 220 /* 221 * Set tenex transfer type. 222 */ 223 /*ARGSUSED*/ 224 void 225 settenex(int argc, char *argv[]) 226 { 227 228 stype[1] = "tenex"; 229 settype(2, stype); 230 } 231 232 /* 233 * Set file transfer mode. 234 */ 235 /*ARGSUSED*/ 236 void 237 setftmode(int argc, char *argv[]) 238 { 239 240 fprintf(ttyout, "We only support %s mode, sorry.\n", modename); 241 code = -1; 242 } 243 244 /* 245 * Set file transfer format. 246 */ 247 /*ARGSUSED*/ 248 void 249 setform(int argc, char *argv[]) 250 { 251 252 fprintf(ttyout, "We only support %s format, sorry.\n", formname); 253 code = -1; 254 } 255 256 /* 257 * Set file transfer structure. 258 */ 259 /*ARGSUSED*/ 260 void 261 setstruct(int argc, char *argv[]) 262 { 263 264 fprintf(ttyout, "We only support %s structure, sorry.\n", structname); 265 code = -1; 266 } 267 268 #ifndef SMALL 269 void 270 reput(int argc, char *argv[]) 271 { 272 273 (void)putit(argc, argv, 1); 274 } 275 #endif /* !SMALL */ 276 277 void 278 put(int argc, char *argv[]) 279 { 280 281 (void)putit(argc, argv, 0); 282 } 283 284 /* 285 * Send a single file. 286 */ 287 void 288 putit(int argc, char *argv[], int restartit) 289 { 290 char *cmd; 291 int loc = 0; 292 char *oldargv1, *oldargv2; 293 294 if (argc == 2) { 295 argc++; 296 argv[2] = argv[1]; 297 loc++; 298 } 299 if (argc < 2 && !another(&argc, &argv, "local-file")) 300 goto usage; 301 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 302 usage: 303 fprintf(ttyout, "usage: %s local-file [remote-file]\n", 304 argv[0]); 305 code = -1; 306 return; 307 } 308 oldargv1 = argv[1]; 309 oldargv2 = argv[2]; 310 if (!globulize(&argv[1])) { 311 code = -1; 312 return; 313 } 314 /* 315 * If "globulize" modifies argv[1], and argv[2] is a copy of 316 * the old argv[1], make it a copy of the new argv[1]. 317 */ 318 if (argv[1] != oldargv1 && argv[2] == oldargv1) { 319 argv[2] = argv[1]; 320 } 321 #ifndef SMALL 322 if (restartit == 1) { 323 if (curtype != type) 324 changetype(type, 0); 325 restart_point = remotesize(argv[2], 1); 326 if (restart_point < 0) { 327 restart_point = 0; 328 code = -1; 329 return; 330 } 331 } 332 #endif /* !SMALL */ 333 if (strcmp(argv[0], "append") == 0) { 334 restartit = 1; 335 } 336 cmd = restartit ? "APPE" : ((sunique) ? "STOU" : "STOR"); 337 if (loc && ntflag) { 338 argv[2] = dotrans(argv[2]); 339 } 340 if (loc && mapflag) { 341 argv[2] = domap(argv[2]); 342 } 343 sendrequest(cmd, argv[1], argv[2], 344 argv[1] != oldargv1 || argv[2] != oldargv2); 345 restart_point = 0; 346 if (oldargv1 != argv[1]) /* free up after globulize() */ 347 free(argv[1]); 348 } 349 350 /* 351 * Send multiple files. 352 */ 353 void 354 mput(int argc, char *argv[]) 355 { 356 extern int optind, optreset; 357 int ch, i, restartit = 0; 358 sig_t oldintr; 359 char *cmd, *tp; 360 361 optind = optreset = 1; 362 363 #ifndef SMALL 364 while ((ch = getopt(argc, argv, "c")) != -1) { 365 switch(ch) { 366 case 'c': 367 restartit = 1; 368 break; 369 default: 370 goto usage; 371 } 372 } 373 #endif /* !SMALL */ 374 375 if (argc - optind < 1 && !another(&argc, &argv, "local-files")) { 376 usage: 377 fprintf(ttyout, "usage: %s [-c] local-files\n", argv[0]); 378 code = -1; 379 return; 380 } 381 382 #ifndef SMALL 383 argv[optind - 1] = argv[0]; 384 argc -= optind - 1; 385 argv += optind - 1; 386 #endif /* !SMALL */ 387 388 mname = argv[0]; 389 mflag = 1; 390 391 oldintr = signal(SIGINT, mabort); 392 (void)setjmp(jabort); 393 if (proxy) { 394 char *cp, *tp2, tmpbuf[MAXPATHLEN]; 395 396 while ((cp = remglob(argv, 0, NULL)) != NULL) { 397 if (*cp == '\0') { 398 mflag = 0; 399 continue; 400 } 401 if (mflag && confirm(argv[0], cp)) { 402 tp = cp; 403 if (mcase) { 404 while (*tp && !islower(*tp)) { 405 tp++; 406 } 407 if (!*tp) { 408 tp = cp; 409 tp2 = tmpbuf; 410 while ((*tp2 = *tp) != '\0') { 411 if (isupper(*tp2)) { 412 *tp2 = 413 tolower(*tp2); 414 } 415 tp++; 416 tp2++; 417 } 418 } 419 tp = tmpbuf; 420 } 421 if (ntflag) { 422 tp = dotrans(tp); 423 } 424 if (mapflag) { 425 tp = domap(tp); 426 } 427 #ifndef SMALL 428 if (restartit == 1) { 429 off_t ret; 430 431 if (curtype != type) 432 changetype(type, 0); 433 ret = remotesize(tp, 0); 434 restart_point = (ret < 0) ? 0 : ret; 435 } 436 #endif /* !SMALL */ 437 cmd = restartit ? "APPE" : ((sunique) ? 438 "STOU" : "STOR"); 439 sendrequest(cmd, cp, tp, 440 cp != tp || !interactive); 441 restart_point = 0; 442 if (!mflag && fromatty) { 443 if (confirm(argv[0], NULL)) 444 mflag = 1; 445 } 446 } 447 } 448 (void)signal(SIGINT, oldintr); 449 mflag = 0; 450 return; 451 } 452 for (i = 1; i < argc; i++) { 453 char **cpp; 454 glob_t gl; 455 int flags; 456 457 if (!doglob) { 458 if (mflag && confirm(argv[0], argv[i])) { 459 tp = (ntflag) ? dotrans(argv[i]) : argv[i]; 460 tp = (mapflag) ? domap(tp) : tp; 461 #ifndef SMALL 462 if (restartit == 1) { 463 off_t ret; 464 465 if (curtype != type) 466 changetype(type, 0); 467 ret = remotesize(tp, 0); 468 restart_point = (ret < 0) ? 0 : ret; 469 } 470 #endif /* !SMALL */ 471 cmd = restartit ? "APPE" : ((sunique) ? 472 "STOU" : "STOR"); 473 sendrequest(cmd, argv[i], tp, 474 tp != argv[i] || !interactive); 475 restart_point = 0; 476 if (!mflag && fromatty) { 477 if (confirm(argv[0], NULL)) 478 mflag = 1; 479 } 480 } 481 continue; 482 } 483 484 memset(&gl, 0, sizeof(gl)); 485 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 486 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { 487 warnx("%s: not found", argv[i]); 488 globfree(&gl); 489 continue; 490 } 491 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) { 492 if (mflag && confirm(argv[0], *cpp)) { 493 tp = (ntflag) ? dotrans(*cpp) : *cpp; 494 tp = (mapflag) ? domap(tp) : tp; 495 #ifndef SMALL 496 if (restartit == 1) { 497 off_t ret; 498 499 if (curtype != type) 500 changetype(type, 0); 501 ret = remotesize(tp, 0); 502 restart_point = (ret < 0) ? 0 : ret; 503 } 504 #endif /* !SMALL */ 505 cmd = restartit ? "APPE" : ((sunique) ? 506 "STOU" : "STOR"); 507 sendrequest(cmd, *cpp, tp, 508 *cpp != tp || !interactive); 509 restart_point = 0; 510 if (!mflag && fromatty) { 511 if (confirm(argv[0], NULL)) 512 mflag = 1; 513 } 514 } 515 } 516 globfree(&gl); 517 } 518 (void)signal(SIGINT, oldintr); 519 mflag = 0; 520 } 521 522 #ifndef SMALL 523 void 524 reget(int argc, char *argv[]) 525 { 526 527 (void)getit(argc, argv, 1, "r+w"); 528 } 529 #endif /* !SMALL */ 530 531 void 532 get(int argc, char *argv[]) 533 { 534 535 (void)getit(argc, argv, 0, restart_point ? "r+w" : "w" ); 536 } 537 538 /* 539 * Receive one file. 540 */ 541 int 542 getit(int argc, char *argv[], int restartit, const char *mode) 543 { 544 int loc = 0; 545 int rval = 0; 546 char *oldargv1, *oldargv2, *globargv2; 547 548 if (argc == 2) { 549 argc++; 550 argv[2] = argv[1]; 551 loc++; 552 } 553 if (argc < 2 && !another(&argc, &argv, "remote-file")) 554 goto usage; 555 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { 556 usage: 557 fprintf(ttyout, "usage: %s remote-file [local-file]\n", 558 argv[0]); 559 code = -1; 560 return (0); 561 } 562 oldargv1 = argv[1]; 563 oldargv2 = argv[2]; 564 if (!globulize(&argv[2])) { 565 code = -1; 566 return (0); 567 } 568 globargv2 = argv[2]; 569 if (loc && mcase) { 570 char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN]; 571 572 while (*tp && !islower(*tp)) { 573 tp++; 574 } 575 if (!*tp) { 576 tp = argv[2]; 577 tp2 = tmpbuf; 578 while ((*tp2 = *tp) != '\0') { 579 if (isupper(*tp2)) { 580 *tp2 = tolower(*tp2); 581 } 582 tp++; 583 tp2++; 584 } 585 argv[2] = tmpbuf; 586 } 587 } 588 if (loc && ntflag) 589 argv[2] = dotrans(argv[2]); 590 if (loc && mapflag) 591 argv[2] = domap(argv[2]); 592 #ifndef SMALL 593 if (restartit) { 594 struct stat stbuf; 595 int ret; 596 597 ret = stat(argv[2], &stbuf); 598 if (restartit == 1) { 599 if (ret < 0) { 600 warn("local: %s", argv[2]); 601 goto freegetit; 602 } 603 restart_point = stbuf.st_size; 604 } else { 605 if (ret == 0) { 606 time_t mtime; 607 608 mtime = remotemodtime(argv[1], 0); 609 if (mtime == -1) 610 goto freegetit; 611 if (stbuf.st_mtime >= mtime) { 612 rval = 1; 613 goto freegetit; 614 } 615 } 616 } 617 } 618 #endif /* !SMALL */ 619 620 recvrequest("RETR", argv[2], argv[1], mode, 621 argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc); 622 restart_point = 0; 623 freegetit: 624 if (oldargv2 != globargv2) /* free up after globulize() */ 625 free(globargv2); 626 return (rval); 627 } 628 629 /* XXX - Signal race. */ 630 /* ARGSUSED */ 631 void 632 mabort(int signo) 633 { 634 alarmtimer(0); 635 putc('\n', ttyout); 636 (void)fflush(ttyout); 637 if (mflag && fromatty) 638 if (confirm(mname, NULL)) 639 longjmp(jabort, 1); 640 mflag = 0; 641 longjmp(jabort, 1); 642 } 643 644 /* 645 * Get multiple files. 646 */ 647 void 648 mget(int argc, char *argv[]) 649 { 650 extern int optind, optreset; 651 sig_t oldintr; 652 int ch, xargc = 2; 653 char *cp, localcwd[MAXPATHLEN], *xargv[] = {argv[0], NULL, NULL}; 654 static int restartit = 0; 655 #ifndef SMALL 656 extern char *optarg; 657 const char *errstr; 658 int i = 1; 659 char type = NULL, *dummyargv[] = {argv[0], ".", NULL}; 660 FILE *ftemp = NULL; 661 static int depth = 0, max_depth = 0; 662 #endif /* !SMALL */ 663 664 optind = optreset = 1; 665 666 #ifndef SMALL 667 668 if (depth) 669 depth++; 670 671 while ((ch = getopt(argc, argv, "cd:nr")) != -1) { 672 switch(ch) { 673 case 'c': 674 restartit = 1; 675 break; 676 case 'd': 677 max_depth = strtonum(optarg, 0, INT_MAX, &errstr); 678 if (errstr != NULL) { 679 fprintf(ttyout, "bad depth value, %s: %s\n", 680 errstr, optarg); 681 code = -1; 682 return; 683 } 684 break; 685 case 'n': 686 restartit = -1; 687 break; 688 case 'r': 689 depth = 1; 690 break; 691 default: 692 goto usage; 693 } 694 } 695 #endif /* !SMALL */ 696 697 if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) { 698 usage: 699 fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n", 700 argv[0]); 701 code = -1; 702 return; 703 } 704 705 #ifndef SMALL 706 argv[optind - 1] = argv[0]; 707 argc -= optind - 1; 708 argv += optind - 1; 709 #endif /* !SMALL */ 710 711 mname = argv[0]; 712 mflag = 1; 713 if (getcwd(localcwd, sizeof(localcwd)) == NULL) 714 err(1, "can't get cwd"); 715 716 oldintr = signal(SIGINT, mabort); 717 (void)setjmp(jabort); 718 while ((cp = 719 #ifndef SMALL 720 depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) : 721 #endif /* !SMALL */ 722 remglob(argv, proxy, NULL)) != NULL 723 #ifndef SMALL 724 || (mflag && depth && ++i < argc) 725 #endif /* !SMALL */ 726 ) { 727 #ifndef SMALL 728 if (cp == NULL) 729 continue; 730 #endif /* !SMALL */ 731 if (*cp == '\0') { 732 mflag = 0; 733 continue; 734 } 735 if (!mflag) 736 continue; 737 #ifndef SMALL 738 if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0) 739 continue; 740 #endif /* !SMALL */ 741 if (!fileindir(cp, localcwd)) { 742 fprintf(ttyout, "Skipping non-relative filename `%s'\n", 743 cp); 744 continue; 745 } 746 #ifndef SMALL 747 if (type == 'd' && depth == max_depth) 748 continue; 749 #endif /* !SMALL */ 750 if (confirm(argv[0], cp)) { 751 #ifndef SMALL 752 if (type == 'd') { 753 mkdir(cp, 0755); 754 if (chdir(cp) != 0) { 755 warn("local: %s", cp); 756 continue; 757 } 758 759 xargv[1] = cp; 760 cd(xargc, xargv); 761 if (dirchange != 1) 762 goto out; 763 764 xargv[1] = "*"; 765 mget(xargc, xargv); 766 767 xargv[1] = ".."; 768 cd(xargc, xargv); 769 if (dirchange != 1) { 770 mflag = 0; 771 goto out; 772 } 773 774 out: 775 if (chdir("..") != 0) { 776 warn("local: %s", cp); 777 mflag = 0; 778 } 779 continue; 780 } 781 if (type == 's') 782 /* Currently ignored. */ 783 continue; 784 #endif /* !SMALL */ 785 xargv[1] = cp; 786 (void)getit(xargc, xargv, restartit, 787 (restartit == 1 || restart_point) ? "r+w" : "w"); 788 if (!mflag && fromatty) { 789 if (confirm(argv[0], NULL)) 790 mflag = 1; 791 } 792 } 793 } 794 (void)signal(SIGINT, oldintr); 795 #ifndef SMALL 796 if (depth) 797 depth--; 798 if (depth == 0 || mflag == 0) 799 depth = max_depth = mflag = restartit = 0; 800 #else /* !SMALL */ 801 mflag = 0; 802 #endif /* !SMALL */ 803 } 804 805 char * 806 onoff(int bool) 807 { 808 809 return (bool ? "on" : "off"); 810 } 811 812 /* 813 * Show status. 814 */ 815 /*ARGSUSED*/ 816 void 817 status(int argc, char *argv[]) 818 { 819 int i; 820 821 if (connected) 822 fprintf(ttyout, "Connected %sto %s.\n", 823 connected == -1 ? "and logged in" : "", hostname); 824 else 825 fputs("Not connected.\n", ttyout); 826 if (!proxy) { 827 pswitch(1); 828 if (connected) { 829 fprintf(ttyout, "Connected for proxy commands to %s.\n", 830 hostname); 831 } 832 else { 833 fputs("No proxy connection.\n", ttyout); 834 } 835 pswitch(0); 836 } 837 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), 838 *gateserver ? gateserver : "(none)", gateport); 839 fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode)); 840 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", 841 modename, typename, formname, structname); 842 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", 843 onoff(verbose), onoff(bell), onoff(interactive), 844 onoff(doglob)); 845 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", onoff(sunique), 846 onoff(runique)); 847 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); 848 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag)); 849 if (ntflag) { 850 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); 851 } 852 else { 853 fputs("Ntrans: off.\n", ttyout); 854 } 855 if (mapflag) { 856 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); 857 } 858 else { 859 fputs("Nmap: off.\n", ttyout); 860 } 861 fprintf(ttyout, "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", 862 onoff(hash), mark, onoff(progress)); 863 fprintf(ttyout, "Use of PORT/LPRT cmds: %s.\n", onoff(sendport)); 864 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), 865 epsv4bad ? " (disabled for this connection)" : ""); 866 #ifndef SMALL 867 fprintf(ttyout, "Command line editing: %s.\n", onoff(editing)); 868 #endif /* !SMALL */ 869 if (macnum > 0) { 870 fputs("Macros:\n", ttyout); 871 for (i=0; i<macnum; i++) { 872 fprintf(ttyout, "\t%s\n", macros[i].mac_name); 873 } 874 } 875 code = 0; 876 } 877 878 /* 879 * Toggle a variable 880 */ 881 int 882 togglevar(int argc, char *argv[], int *var, const char *mesg) 883 { 884 if (argc < 2) { 885 *var = !*var; 886 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { 887 *var = 1; 888 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { 889 *var = 0; 890 } else { 891 fprintf(ttyout, "usage: %s [on | off]\n", argv[0]); 892 return (-1); 893 } 894 if (mesg) 895 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); 896 return (*var); 897 } 898 899 /* 900 * Set beep on cmd completed mode. 901 */ 902 /*ARGSUSED*/ 903 void 904 setbell(int argc, char *argv[]) 905 { 906 907 code = togglevar(argc, argv, &bell, "Bell mode"); 908 } 909 910 /* 911 * Set command line editing 912 */ 913 #ifndef SMALL 914 /*ARGSUSED*/ 915 void 916 setedit(int argc, char *argv[]) 917 { 918 919 code = togglevar(argc, argv, &editing, "Editing mode"); 920 controlediting(); 921 } 922 #endif /* !SMALL */ 923 924 /* 925 * Toggle use of IPv4 EPSV/EPRT 926 */ 927 /*ARGSUSED*/ 928 void 929 setepsv4(int argc, char *argv[]) 930 { 931 932 code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4"); 933 epsv4bad = 0; 934 } 935 936 /* 937 * Turn on packet tracing. 938 */ 939 /*ARGSUSED*/ 940 void 941 settrace(int argc, char *argv[]) 942 { 943 944 code = togglevar(argc, argv, &trace, "Packet tracing"); 945 } 946 947 /* 948 * Toggle hash mark printing during transfers, or set hash mark bytecount. 949 */ 950 /*ARGSUSED*/ 951 void 952 sethash(int argc, char *argv[]) 953 { 954 if (argc == 1) 955 hash = !hash; 956 else if (argc != 2) { 957 fprintf(ttyout, "usage: %s [on | off | size]\n", argv[0]); 958 code = -1; 959 return; 960 } else if (strcasecmp(argv[1], "on") == 0) 961 hash = 1; 962 else if (strcasecmp(argv[1], "off") == 0) 963 hash = 0; 964 else { 965 int nmark; 966 const char *errstr; 967 968 nmark = strtonum(argv[1], 1, INT_MAX, &errstr); 969 if (errstr) { 970 fprintf(ttyout, "bytecount value is %s: %s\n", 971 errstr, argv[1]); 972 code = -1; 973 return; 974 } 975 mark = nmark; 976 hash = 1; 977 } 978 fprintf(ttyout, "Hash mark printing %s", onoff(hash)); 979 if (hash) 980 fprintf(ttyout, " (%d bytes/hash mark)", mark); 981 fputs(".\n", ttyout); 982 code = hash; 983 } 984 985 /* 986 * Turn on printing of server echo's. 987 */ 988 /*ARGSUSED*/ 989 void 990 setverbose(int argc, char *argv[]) 991 { 992 993 code = togglevar(argc, argv, &verbose, "Verbose mode"); 994 } 995 996 /* 997 * Toggle PORT/LPRT cmd use before each data connection. 998 */ 999 /*ARGSUSED*/ 1000 void 1001 setport(int argc, char *argv[]) 1002 { 1003 1004 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); 1005 } 1006 1007 /* 1008 * Toggle transfer progress bar. 1009 */ 1010 /*ARGSUSED*/ 1011 void 1012 setprogress(int argc, char *argv[]) 1013 { 1014 1015 code = togglevar(argc, argv, &progress, "Progress bar"); 1016 } 1017 1018 /* 1019 * Turn on interactive prompting during mget, mput, and mdelete. 1020 */ 1021 /*ARGSUSED*/ 1022 void 1023 setprompt(int argc, char *argv[]) 1024 { 1025 1026 code = togglevar(argc, argv, &interactive, "Interactive mode"); 1027 } 1028 1029 /* 1030 * Toggle gate-ftp mode, or set gate-ftp server 1031 */ 1032 /*ARGSUSED*/ 1033 void 1034 setgate(int argc, char *argv[]) 1035 { 1036 static char gsbuf[MAXHOSTNAMELEN]; 1037 1038 if (argc > 3) { 1039 fprintf(ttyout, "usage: %s [on | off | host [port]]\n", 1040 argv[0]); 1041 code = -1; 1042 return; 1043 } else if (argc < 2) { 1044 gatemode = !gatemode; 1045 } else { 1046 if (argc == 2 && strcasecmp(argv[1], "on") == 0) 1047 gatemode = 1; 1048 else if (argc == 2 && strcasecmp(argv[1], "off") == 0) 1049 gatemode = 0; 1050 else { 1051 if (argc == 3) { 1052 gateport = strdup(argv[2]); 1053 if (gateport == NULL) 1054 err(1, NULL); 1055 } 1056 strlcpy(gsbuf, argv[1], sizeof(gsbuf)); 1057 gateserver = gsbuf; 1058 gatemode = 1; 1059 } 1060 } 1061 if (gatemode && (gateserver == NULL || *gateserver == '\0')) { 1062 fprintf(ttyout, 1063 "Disabling gate-ftp mode - no gate-ftp server defined.\n"); 1064 gatemode = 0; 1065 } else { 1066 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", 1067 onoff(gatemode), 1068 *gateserver ? gateserver : "(none)", gateport); 1069 } 1070 code = gatemode; 1071 } 1072 1073 /* 1074 * Toggle metacharacter interpretation on local file names. 1075 */ 1076 /*ARGSUSED*/ 1077 void 1078 setglob(int argc, char *argv[]) 1079 { 1080 1081 code = togglevar(argc, argv, &doglob, "Globbing"); 1082 } 1083 1084 /* 1085 * Toggle preserving modification times on retrieved files. 1086 */ 1087 /*ARGSUSED*/ 1088 void 1089 setpreserve(int argc, char *argv[]) 1090 { 1091 1092 code = togglevar(argc, argv, &preserve, "Preserve modification times"); 1093 } 1094 1095 /* 1096 * Set debugging mode on/off and/or set level of debugging. 1097 */ 1098 #ifndef SMALL 1099 /*ARGSUSED*/ 1100 void 1101 setdebug(int argc, char *argv[]) 1102 { 1103 if (argc > 2) { 1104 fprintf(ttyout, "usage: %s [on | off | debuglevel]\n", argv[0]); 1105 code = -1; 1106 return; 1107 } else if (argc == 2) { 1108 if (strcasecmp(argv[1], "on") == 0) 1109 debug = 1; 1110 else if (strcasecmp(argv[1], "off") == 0) 1111 debug = 0; 1112 else { 1113 const char *errstr; 1114 int val; 1115 1116 val = strtonum(argv[1], 0, INT_MAX, &errstr); 1117 if (errstr) { 1118 fprintf(ttyout, "debugging value is %s: %s\n", 1119 errstr, argv[1]); 1120 code = -1; 1121 return; 1122 } 1123 debug = val; 1124 } 1125 } else 1126 debug = !debug; 1127 if (debug) 1128 options |= SO_DEBUG; 1129 else 1130 options &= ~SO_DEBUG; 1131 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug); 1132 code = debug > 0; 1133 } 1134 #endif /* !SMALL */ 1135 1136 /* 1137 * Set current working directory on remote machine. 1138 */ 1139 void 1140 cd(int argc, char *argv[]) 1141 { 1142 int r; 1143 1144 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) || 1145 argc > 2) { 1146 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); 1147 code = -1; 1148 return; 1149 } 1150 r = command("CWD %s", argv[1]); 1151 if (r == ERROR && code == 500) { 1152 if (verbose) 1153 fputs("CWD command not recognized, trying XCWD.\n", ttyout); 1154 r = command("XCWD %s", argv[1]); 1155 } 1156 if (r == ERROR && code == 550) { 1157 dirchange = 0; 1158 return; 1159 } 1160 if (r == COMPLETE) 1161 dirchange = 1; 1162 } 1163 1164 /* 1165 * Set current working directory on local machine. 1166 */ 1167 void 1168 lcd(int argc, char *argv[]) 1169 { 1170 char buf[MAXPATHLEN]; 1171 char *oldargv1; 1172 1173 if (argc < 2) 1174 argc++, argv[1] = home; 1175 if (argc != 2) { 1176 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]); 1177 code = -1; 1178 return; 1179 } 1180 oldargv1 = argv[1]; 1181 if (!globulize(&argv[1])) { 1182 code = -1; 1183 return; 1184 } 1185 if (chdir(argv[1]) < 0) { 1186 warn("local: %s", argv[1]); 1187 code = -1; 1188 } else { 1189 if (getcwd(buf, sizeof(buf)) != NULL) 1190 fprintf(ttyout, "Local directory now %s\n", buf); 1191 else 1192 warn("getcwd: %s", argv[1]); 1193 code = 0; 1194 } 1195 if (oldargv1 != argv[1]) /* free up after globulize() */ 1196 free(argv[1]); 1197 } 1198 1199 /* 1200 * Delete a single file. 1201 */ 1202 void 1203 deletecmd(int argc, char *argv[]) 1204 { 1205 1206 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) { 1207 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 1208 code = -1; 1209 return; 1210 } 1211 (void)command("DELE %s", argv[1]); 1212 } 1213 1214 /* 1215 * Delete multiple files. 1216 */ 1217 void 1218 mdelete(int argc, char *argv[]) 1219 { 1220 sig_t oldintr; 1221 char *cp; 1222 1223 if (argc < 2 && !another(&argc, &argv, "remote-files")) { 1224 fprintf(ttyout, "usage: %s remote-files\n", argv[0]); 1225 code = -1; 1226 return; 1227 } 1228 mname = argv[0]; 1229 mflag = 1; 1230 oldintr = signal(SIGINT, mabort); 1231 (void)setjmp(jabort); 1232 while ((cp = remglob(argv, 0, NULL)) != NULL) { 1233 if (*cp == '\0') { 1234 mflag = 0; 1235 continue; 1236 } 1237 if (mflag && confirm(argv[0], cp)) { 1238 (void)command("DELE %s", cp); 1239 if (!mflag && fromatty) { 1240 if (confirm(argv[0], NULL)) 1241 mflag = 1; 1242 } 1243 } 1244 } 1245 (void)signal(SIGINT, oldintr); 1246 mflag = 0; 1247 } 1248 1249 /* 1250 * Rename a remote file. 1251 */ 1252 void 1253 renamefile(int argc, char *argv[]) 1254 { 1255 1256 if (argc < 2 && !another(&argc, &argv, "from-name")) 1257 goto usage; 1258 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { 1259 usage: 1260 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]); 1261 code = -1; 1262 return; 1263 } 1264 if (command("RNFR %s", argv[1]) == CONTINUE) 1265 (void)command("RNTO %s", argv[2]); 1266 } 1267 1268 /* 1269 * Get a directory listing of remote files. 1270 */ 1271 void 1272 ls(int argc, char *argv[]) 1273 { 1274 const char *cmd; 1275 char *oldargv2, *globargv2; 1276 1277 if (argc < 2) 1278 argc++, argv[1] = NULL; 1279 if (argc < 3) 1280 argc++, argv[2] = "-"; 1281 if (argc > 3) { 1282 fprintf(ttyout, "usage: %s [remote-directory [local-file]]\n", 1283 argv[0]); 1284 code = -1; 1285 return; 1286 } 1287 cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST"; 1288 oldargv2 = argv[2]; 1289 if (strcmp(argv[2], "-") && !globulize(&argv[2])) { 1290 code = -1; 1291 return; 1292 } 1293 globargv2 = argv[2]; 1294 if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) || 1295 !confirm("output to local-file:", argv[2]))) { 1296 code = -1; 1297 goto freels; 1298 } 1299 recvrequest(cmd, argv[2], argv[1], "w", 0, 0); 1300 1301 /* flush results in case commands are coming from a pipe */ 1302 fflush(ttyout); 1303 freels: 1304 if (argv[2] != globargv2) /* free up after globulize() */ 1305 free(argv[2]); 1306 if (globargv2 != oldargv2) 1307 free(globargv2); 1308 } 1309 1310 /* 1311 * Get a directory listing of multiple remote files. 1312 */ 1313 void 1314 mls(int argc, char *argv[]) 1315 { 1316 sig_t oldintr; 1317 int i; 1318 char lmode[1], *dest, *odest; 1319 1320 if (argc < 2 && !another(&argc, &argv, "remote-files")) 1321 goto usage; 1322 if (argc < 3 && !another(&argc, &argv, "local-file")) { 1323 usage: 1324 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]); 1325 code = -1; 1326 return; 1327 } 1328 odest = dest = argv[argc - 1]; 1329 argv[argc - 1] = NULL; 1330 if (strcmp(dest, "-") && *dest != '|') 1331 if (!globulize(&dest) || 1332 !confirm("output to local-file:", dest)) { 1333 code = -1; 1334 return; 1335 } 1336 mname = argv[0]; 1337 mflag = 1; 1338 oldintr = signal(SIGINT, mabort); 1339 (void)setjmp(jabort); 1340 for (i = 1; mflag && i < argc-1; ++i) { 1341 *lmode = (i == 1) ? 'w' : 'a'; 1342 recvrequest("LIST", dest, argv[i], lmode, 0, 0); 1343 if (!mflag && fromatty) { 1344 if (confirm(argv[0], NULL)) 1345 mflag ++; 1346 } 1347 } 1348 (void)signal(SIGINT, oldintr); 1349 mflag = 0; 1350 if (dest != odest) /* free up after globulize() */ 1351 free(dest); 1352 } 1353 1354 /* 1355 * Do a shell escape 1356 */ 1357 /*ARGSUSED*/ 1358 void 1359 shell(int argc, char *argv[]) 1360 { 1361 pid_t pid; 1362 sig_t old1, old2; 1363 char shellnam[MAXPATHLEN], *shellp, *namep; 1364 int wait_status; 1365 1366 old1 = signal (SIGINT, SIG_IGN); 1367 old2 = signal (SIGQUIT, SIG_IGN); 1368 if ((pid = fork()) == 0) { 1369 for (pid = 3; pid < 20; pid++) 1370 (void)close(pid); 1371 (void)signal(SIGINT, SIG_DFL); 1372 (void)signal(SIGQUIT, SIG_DFL); 1373 shellp = getenv("SHELL"); 1374 if (shellp == NULL || *shellp == '\0') 1375 shellp = _PATH_BSHELL; 1376 namep = strrchr(shellp, '/'); 1377 if (namep == NULL) 1378 namep = shellp; 1379 shellnam[0] = '-'; 1380 (void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1); 1381 if (strcmp(namep, "sh") != 0) 1382 shellnam[0] = '+'; 1383 #ifndef SMALL 1384 if (debug) { 1385 fputs(shellp, ttyout); 1386 fputc('\n', ttyout); 1387 (void)fflush(ttyout); 1388 } 1389 #endif /* !SMALL */ 1390 if (argc > 1) { 1391 execl(shellp, shellnam, "-c", altarg, (char *)0); 1392 } 1393 else { 1394 execl(shellp, shellnam, (char *)0); 1395 } 1396 warn("%s", shellp); 1397 code = -1; 1398 exit(1); 1399 } 1400 if (pid > 0) 1401 while (wait(&wait_status) != pid) 1402 ; 1403 (void)signal(SIGINT, old1); 1404 (void)signal(SIGQUIT, old2); 1405 if (pid == -1) { 1406 warn("Try again later"); 1407 code = -1; 1408 } 1409 else { 1410 code = 0; 1411 } 1412 } 1413 1414 /* 1415 * Send new user information (re-login) 1416 */ 1417 void 1418 user(int argc, char *argv[]) 1419 { 1420 char acctname[80]; 1421 int n, aflag = 0; 1422 1423 if (argc < 2) 1424 (void)another(&argc, &argv, "username"); 1425 if (argc < 2 || argc > 4) { 1426 fprintf(ttyout, "usage: %s username [password [account]]\n", 1427 argv[0]); 1428 code = -1; 1429 return; 1430 } 1431 n = command("USER %s", argv[1]); 1432 if (n == CONTINUE) { 1433 if (argc < 3 ) 1434 argv[2] = getpass("Password:"), argc++; 1435 n = command("PASS %s", argv[2]); 1436 } 1437 if (n == CONTINUE) { 1438 if (argc < 4) { 1439 (void)fputs("Account: ", ttyout); 1440 (void)fflush(ttyout); 1441 if (fgets(acctname, sizeof(acctname), stdin) == NULL) { 1442 clearerr(stdin); 1443 goto fail; 1444 } 1445 1446 acctname[strcspn(acctname, "\n")] = '\0'; 1447 1448 argv[3] = acctname; 1449 argc++; 1450 } 1451 n = command("ACCT %s", argv[3]); 1452 aflag++; 1453 } 1454 if (n != COMPLETE) { 1455 fail: 1456 fputs("Login failed.\n", ttyout); 1457 return; 1458 } 1459 if (!aflag && argc == 4) { 1460 (void)command("ACCT %s", argv[3]); 1461 } 1462 connected = -1; 1463 } 1464 1465 /* 1466 * Print working directory on remote machine. 1467 */ 1468 /*ARGSUSED*/ 1469 void 1470 pwd(int argc, char *argv[]) 1471 { 1472 int oldverbose = verbose; 1473 1474 /* 1475 * If we aren't verbose, this doesn't do anything! 1476 */ 1477 verbose = 1; 1478 if (command("PWD") == ERROR && code == 500) { 1479 fputs("PWD command not recognized, trying XPWD.\n", ttyout); 1480 (void)command("XPWD"); 1481 } 1482 verbose = oldverbose; 1483 } 1484 1485 /* 1486 * Print working directory on local machine. 1487 */ 1488 /* ARGSUSED */ 1489 void 1490 lpwd(int argc, char *argv[]) 1491 { 1492 char buf[MAXPATHLEN]; 1493 1494 if (getcwd(buf, sizeof(buf)) != NULL) 1495 fprintf(ttyout, "Local directory %s\n", buf); 1496 else 1497 warn("getcwd"); 1498 code = 0; 1499 } 1500 1501 /* 1502 * Make a directory. 1503 */ 1504 void 1505 makedir(int argc, char *argv[]) 1506 { 1507 1508 if ((argc < 2 && !another(&argc, &argv, "directory-name")) || 1509 argc > 2) { 1510 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1511 code = -1; 1512 return; 1513 } 1514 if (command("MKD %s", argv[1]) == ERROR && code == 500) { 1515 if (verbose) 1516 fputs("MKD command not recognized, trying XMKD.\n", ttyout); 1517 (void)command("XMKD %s", argv[1]); 1518 } 1519 } 1520 1521 /* 1522 * Remove a directory. 1523 */ 1524 void 1525 removedir(int argc, char *argv[]) 1526 { 1527 1528 if ((argc < 2 && !another(&argc, &argv, "directory-name")) || 1529 argc > 2) { 1530 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1531 code = -1; 1532 return; 1533 } 1534 if (command("RMD %s", argv[1]) == ERROR && code == 500) { 1535 if (verbose) 1536 fputs("RMD command not recognized, trying XRMD.\n", ttyout); 1537 (void)command("XRMD %s", argv[1]); 1538 } 1539 } 1540 1541 /* 1542 * Send a line, verbatim, to the remote machine. 1543 */ 1544 void 1545 quote(int argc, char *argv[]) 1546 { 1547 1548 if (argc < 2 && !another(&argc, &argv, "command line to send")) { 1549 fprintf(ttyout, "usage: %s arg ...\n", argv[0]); 1550 code = -1; 1551 return; 1552 } 1553 quote1("", argc, argv); 1554 } 1555 1556 /* 1557 * Send a SITE command to the remote machine. The line 1558 * is sent verbatim to the remote machine, except that the 1559 * word "SITE" is added at the front. 1560 */ 1561 void 1562 site(int argc, char *argv[]) 1563 { 1564 1565 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) { 1566 fprintf(ttyout, "usage: %s arg ...\n", argv[0]); 1567 code = -1; 1568 return; 1569 } 1570 quote1("SITE", argc, argv); 1571 } 1572 1573 /* 1574 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1575 * Send the result as a one-line command and get response. 1576 */ 1577 void 1578 quote1(const char *initial, int argc, char *argv[]) 1579 { 1580 int i, len; 1581 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1582 1583 (void)strlcpy(buf, initial, sizeof(buf)); 1584 if (argc > 1) { 1585 for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) { 1586 /* Space for next arg */ 1587 if (len > 1) 1588 buf[len++] = ' '; 1589 1590 /* Sanity check */ 1591 if (len >= sizeof(buf) - 1) 1592 break; 1593 1594 /* Copy next argument, NUL terminate always */ 1595 strlcpy(&buf[len], argv[i], sizeof(buf) - len); 1596 1597 /* Update string length */ 1598 len = strlen(buf); 1599 } 1600 } 1601 1602 /* Make double (triple?) sure the sucker is NUL terminated */ 1603 buf[sizeof(buf) - 1] = '\0'; 1604 1605 if (command("%s", buf) == PRELIM) { 1606 while (getreply(0) == PRELIM) 1607 continue; 1608 } 1609 } 1610 1611 void 1612 do_chmod(int argc, char *argv[]) 1613 { 1614 1615 if (argc < 2 && !another(&argc, &argv, "mode")) 1616 goto usage; 1617 if ((argc < 3 && !another(&argc, &argv, "file")) || argc > 3) { 1618 usage: 1619 fprintf(ttyout, "usage: %s mode file\n", argv[0]); 1620 code = -1; 1621 return; 1622 } 1623 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1624 } 1625 1626 void 1627 do_umask(int argc, char *argv[]) 1628 { 1629 int oldverbose = verbose; 1630 1631 verbose = 1; 1632 (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); 1633 verbose = oldverbose; 1634 } 1635 1636 void 1637 idle(int argc, char *argv[]) 1638 { 1639 int oldverbose = verbose; 1640 1641 verbose = 1; 1642 (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); 1643 verbose = oldverbose; 1644 } 1645 1646 /* 1647 * Ask the other side for help. 1648 */ 1649 void 1650 rmthelp(int argc, char *argv[]) 1651 { 1652 int oldverbose = verbose; 1653 1654 verbose = 1; 1655 (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]); 1656 verbose = oldverbose; 1657 } 1658 1659 /* 1660 * Terminate session and exit. 1661 */ 1662 /*ARGSUSED*/ 1663 void 1664 quit(int argc, char *argv[]) 1665 { 1666 1667 if (connected) 1668 disconnect(0, 0); 1669 pswitch(1); 1670 if (connected) { 1671 disconnect(0, 0); 1672 } 1673 exit(0); 1674 } 1675 1676 /* 1677 * Terminate session, but don't exit. 1678 */ 1679 /* ARGSUSED */ 1680 void 1681 disconnect(int argc, char *argv[]) 1682 { 1683 1684 if (!connected) 1685 return; 1686 (void)command("QUIT"); 1687 if (cout) { 1688 (void)fclose(cout); 1689 } 1690 cout = NULL; 1691 connected = 0; 1692 data = -1; 1693 if (!proxy) { 1694 macnum = 0; 1695 } 1696 } 1697 1698 void 1699 account(int argc, char *argv[]) 1700 { 1701 char *ap; 1702 1703 if (argc > 2) { 1704 fprintf(ttyout, "usage: %s [password]\n", argv[0]); 1705 code = -1; 1706 return; 1707 } 1708 else if (argc == 2) 1709 ap = argv[1]; 1710 else 1711 ap = getpass("Account:"); 1712 (void)command("ACCT %s", ap); 1713 } 1714 1715 jmp_buf abortprox; 1716 1717 /* ARGSUSED */ 1718 void 1719 proxabort(int signo) 1720 { 1721 1722 alarmtimer(0); 1723 if (!proxy) { 1724 pswitch(1); 1725 } 1726 if (connected) { 1727 proxflag = 1; 1728 } 1729 else { 1730 proxflag = 0; 1731 } 1732 pswitch(0); 1733 longjmp(abortprox, 1); 1734 } 1735 1736 void 1737 doproxy(int argc, char *argv[]) 1738 { 1739 struct cmd *c; 1740 int cmdpos; 1741 sig_t oldintr; 1742 1743 if (argc < 2 && !another(&argc, &argv, "command")) { 1744 fprintf(ttyout, "usage: %s command\n", argv[0]); 1745 code = -1; 1746 return; 1747 } 1748 c = getcmd(argv[1]); 1749 if (c == (struct cmd *) -1) { 1750 fputs("?Ambiguous command.\n", ttyout); 1751 (void)fflush(ttyout); 1752 code = -1; 1753 return; 1754 } 1755 if (c == 0) { 1756 fputs("?Invalid command.\n", ttyout); 1757 (void)fflush(ttyout); 1758 code = -1; 1759 return; 1760 } 1761 if (!c->c_proxy) { 1762 fputs("?Invalid proxy command.\n", ttyout); 1763 (void)fflush(ttyout); 1764 code = -1; 1765 return; 1766 } 1767 if (setjmp(abortprox)) { 1768 code = -1; 1769 return; 1770 } 1771 oldintr = signal(SIGINT, proxabort); 1772 pswitch(1); 1773 if (c->c_conn && !connected) { 1774 fputs("Not connected.\n", ttyout); 1775 (void)fflush(ttyout); 1776 pswitch(0); 1777 (void)signal(SIGINT, oldintr); 1778 code = -1; 1779 return; 1780 } 1781 cmdpos = strcspn(line, " \t"); 1782 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1783 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1784 (*c->c_handler)(argc-1, argv+1); 1785 if (connected) { 1786 proxflag = 1; 1787 } 1788 else { 1789 proxflag = 0; 1790 } 1791 pswitch(0); 1792 (void)signal(SIGINT, oldintr); 1793 } 1794 1795 void 1796 setcase(int argc, char *argv[]) 1797 { 1798 1799 code = togglevar(argc, argv, &mcase, "Case mapping"); 1800 } 1801 1802 void 1803 setcr(int argc, char *argv[]) 1804 { 1805 1806 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1807 } 1808 1809 void 1810 setntrans(int argc, char *argv[]) 1811 { 1812 if (argc == 1) { 1813 ntflag = 0; 1814 fputs("Ntrans off.\n", ttyout); 1815 code = ntflag; 1816 return; 1817 } 1818 ntflag++; 1819 code = ntflag; 1820 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1821 if (argc == 2) { 1822 ntout[0] = '\0'; 1823 return; 1824 } 1825 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1826 } 1827 1828 char * 1829 dotrans(char *name) 1830 { 1831 static char new[MAXPATHLEN]; 1832 char *cp1, *cp2 = new; 1833 int i, ostop, found; 1834 1835 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 1836 continue; 1837 for (cp1 = name; *cp1; cp1++) { 1838 found = 0; 1839 for (i = 0; *(ntin + i) && i < 16; i++) { 1840 if (*cp1 == *(ntin + i)) { 1841 found++; 1842 if (i < ostop) { 1843 *cp2++ = *(ntout + i); 1844 } 1845 break; 1846 } 1847 } 1848 if (!found) { 1849 *cp2++ = *cp1; 1850 } 1851 } 1852 *cp2 = '\0'; 1853 return (new); 1854 } 1855 1856 void 1857 setnmap(int argc, char *argv[]) 1858 { 1859 char *cp; 1860 1861 if (argc == 1) { 1862 mapflag = 0; 1863 fputs("Nmap off.\n", ttyout); 1864 code = mapflag; 1865 return; 1866 } 1867 if ((argc < 3 && !another(&argc, &argv, "outpattern")) || argc > 3) { 1868 fprintf(ttyout, "usage: %s [inpattern outpattern]\n", argv[0]); 1869 code = -1; 1870 return; 1871 } 1872 mapflag = 1; 1873 code = 1; 1874 cp = strchr(altarg, ' '); 1875 if (proxy) { 1876 while(*++cp == ' ') 1877 continue; 1878 altarg = cp; 1879 cp = strchr(altarg, ' '); 1880 } 1881 *cp = '\0'; 1882 (void)strncpy(mapin, altarg, MAXPATHLEN - 1); 1883 while (*++cp == ' ') 1884 continue; 1885 (void)strncpy(mapout, cp, MAXPATHLEN - 1); 1886 } 1887 1888 char * 1889 domap(char *name) 1890 { 1891 static char new[MAXPATHLEN]; 1892 char *cp1 = name, *cp2 = mapin; 1893 char *tp[9], *te[9]; 1894 int i, toks[9], toknum = 0, match = 1; 1895 1896 for (i=0; i < 9; ++i) { 1897 toks[i] = 0; 1898 } 1899 while (match && *cp1 && *cp2) { 1900 switch (*cp2) { 1901 case '\\': 1902 if (*++cp2 != *cp1) { 1903 match = 0; 1904 } 1905 break; 1906 case '$': 1907 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 1908 if (*cp1 != *(++cp2+1)) { 1909 toks[toknum = *cp2 - '1']++; 1910 tp[toknum] = cp1; 1911 while (*++cp1 && *(cp2+1) 1912 != *cp1); 1913 te[toknum] = cp1; 1914 } 1915 cp2++; 1916 break; 1917 } 1918 /* FALLTHROUGH */ 1919 default: 1920 if (*cp2 != *cp1) { 1921 match = 0; 1922 } 1923 break; 1924 } 1925 if (match && *cp1) { 1926 cp1++; 1927 } 1928 if (match && *cp2) { 1929 cp2++; 1930 } 1931 } 1932 if (!match && *cp1) /* last token mismatch */ 1933 { 1934 toks[toknum] = 0; 1935 } 1936 cp1 = new; 1937 *cp1 = '\0'; 1938 cp2 = mapout; 1939 while (*cp2) { 1940 match = 0; 1941 switch (*cp2) { 1942 case '\\': 1943 if (*(cp2 + 1)) { 1944 *cp1++ = *++cp2; 1945 } 1946 break; 1947 case '[': 1948 LOOP: 1949 if (*++cp2 == '$' && isdigit(*(cp2+1))) { 1950 if (*++cp2 == '0') { 1951 char *cp3 = name; 1952 1953 while (*cp3) { 1954 *cp1++ = *cp3++; 1955 } 1956 match = 1; 1957 } 1958 else if (toks[toknum = *cp2 - '1']) { 1959 char *cp3 = tp[toknum]; 1960 1961 while (cp3 != te[toknum]) { 1962 *cp1++ = *cp3++; 1963 } 1964 match = 1; 1965 } 1966 } 1967 else { 1968 while (*cp2 && *cp2 != ',' && 1969 *cp2 != ']') { 1970 if (*cp2 == '\\') { 1971 cp2++; 1972 } 1973 else if (*cp2 == '$' && 1974 isdigit(*(cp2+1))) { 1975 if (*++cp2 == '0') { 1976 char *cp3 = name; 1977 1978 while (*cp3) { 1979 *cp1++ = *cp3++; 1980 } 1981 } 1982 else if (toks[toknum = 1983 *cp2 - '1']) { 1984 char *cp3=tp[toknum]; 1985 1986 while (cp3 != 1987 te[toknum]) { 1988 *cp1++ = *cp3++; 1989 } 1990 } 1991 } 1992 else if (*cp2) { 1993 *cp1++ = *cp2++; 1994 } 1995 } 1996 if (!*cp2) { 1997 fputs( 1998 "nmap: unbalanced brackets.\n", ttyout); 1999 return (name); 2000 } 2001 match = 1; 2002 cp2--; 2003 } 2004 if (match) { 2005 while (*++cp2 && *cp2 != ']') { 2006 if (*cp2 == '\\' && *(cp2 + 1)) { 2007 cp2++; 2008 } 2009 } 2010 if (!*cp2) { 2011 fputs( 2012 "nmap: unbalanced brackets.\n", ttyout); 2013 return (name); 2014 } 2015 break; 2016 } 2017 switch (*++cp2) { 2018 case ',': 2019 goto LOOP; 2020 case ']': 2021 break; 2022 default: 2023 cp2--; 2024 goto LOOP; 2025 } 2026 break; 2027 case '$': 2028 if (isdigit(*(cp2 + 1))) { 2029 if (*++cp2 == '0') { 2030 char *cp3 = name; 2031 2032 while (*cp3) { 2033 *cp1++ = *cp3++; 2034 } 2035 } 2036 else if (toks[toknum = *cp2 - '1']) { 2037 char *cp3 = tp[toknum]; 2038 2039 while (cp3 != te[toknum]) { 2040 *cp1++ = *cp3++; 2041 } 2042 } 2043 break; 2044 } 2045 /* FALLTHROUGH */ 2046 default: 2047 *cp1++ = *cp2; 2048 break; 2049 } 2050 cp2++; 2051 } 2052 *cp1 = '\0'; 2053 if (!*new) { 2054 return (name); 2055 } 2056 return (new); 2057 } 2058 2059 void 2060 setpassive(int argc, char *argv[]) 2061 { 2062 2063 code = togglevar(argc, argv, &passivemode, 2064 verbose ? "Passive mode" : NULL); 2065 } 2066 2067 void 2068 setsunique(int argc, char *argv[]) 2069 { 2070 2071 code = togglevar(argc, argv, &sunique, "Store unique"); 2072 } 2073 2074 void 2075 setrunique(int argc, char *argv[]) 2076 { 2077 2078 code = togglevar(argc, argv, &runique, "Receive unique"); 2079 } 2080 2081 /* change directory to parent directory */ 2082 /* ARGSUSED */ 2083 void 2084 cdup(int argc, char *argv[]) 2085 { 2086 int r; 2087 2088 r = command("CDUP"); 2089 if (r == ERROR && code == 500) { 2090 if (verbose) 2091 fputs("CDUP command not recognized, trying XCUP.\n", ttyout); 2092 r = command("XCUP"); 2093 } 2094 if (r == COMPLETE) 2095 dirchange = 1; 2096 } 2097 2098 /* 2099 * Restart transfer at specific point 2100 */ 2101 void 2102 restart(int argc, char *argv[]) 2103 { 2104 quad_t nrestart_point; 2105 char *ep; 2106 2107 if (argc != 2) 2108 fputs("restart: offset not specified.\n", ttyout); 2109 else { 2110 nrestart_point = strtoq(argv[1], &ep, 10); 2111 if (nrestart_point == QUAD_MAX || *ep != '\0') 2112 fputs("restart: invalid offset.\n", ttyout); 2113 else { 2114 fprintf(ttyout, "Restarting at %lld. Execute get, put " 2115 "or append to initiate transfer\n", 2116 (long long)nrestart_point); 2117 restart_point = nrestart_point; 2118 } 2119 } 2120 } 2121 2122 /* 2123 * Show remote system type 2124 */ 2125 /* ARGSUSED */ 2126 void 2127 syst(int argc, char *argv[]) 2128 { 2129 2130 (void)command("SYST"); 2131 } 2132 2133 void 2134 macdef(int argc, char *argv[]) 2135 { 2136 char *tmp; 2137 int c; 2138 2139 if (macnum == 16) { 2140 fputs("Limit of 16 macros have already been defined.\n", ttyout); 2141 code = -1; 2142 return; 2143 } 2144 if ((argc < 2 && !another(&argc, &argv, "macro-name")) || argc > 2) { 2145 fprintf(ttyout, "usage: %s macro-name\n", argv[0]); 2146 code = -1; 2147 return; 2148 } 2149 if (interactive) 2150 fputs( 2151 "Enter macro line by line, terminating it with a null line.\n", ttyout); 2152 (void)strlcpy(macros[macnum].mac_name, argv[1], 2153 sizeof(macros[macnum].mac_name)); 2154 if (macnum == 0) 2155 macros[macnum].mac_start = macbuf; 2156 else 2157 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2158 tmp = macros[macnum].mac_start; 2159 while (tmp != macbuf+4096) { 2160 if ((c = getchar()) == EOF) { 2161 fputs("macdef: end of file encountered.\n", ttyout); 2162 code = -1; 2163 return; 2164 } 2165 if ((*tmp = c) == '\n') { 2166 if (tmp == macros[macnum].mac_start) { 2167 macros[macnum++].mac_end = tmp; 2168 code = 0; 2169 return; 2170 } 2171 if (*(tmp-1) == '\0') { 2172 macros[macnum++].mac_end = tmp - 1; 2173 code = 0; 2174 return; 2175 } 2176 *tmp = '\0'; 2177 } 2178 tmp++; 2179 } 2180 while (1) { 2181 while ((c = getchar()) != '\n' && c != EOF) 2182 /* LOOP */; 2183 if (c == EOF || getchar() == '\n') { 2184 fputs("Macro not defined - 4K buffer exceeded.\n", ttyout); 2185 code = -1; 2186 return; 2187 } 2188 } 2189 } 2190 2191 /* 2192 * Get size of file on remote machine 2193 */ 2194 void 2195 sizecmd(int argc, char *argv[]) 2196 { 2197 off_t size; 2198 2199 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 2200 fprintf(ttyout, "usage: %s file\n", argv[0]); 2201 code = -1; 2202 return; 2203 } 2204 size = remotesize(argv[1], 1); 2205 if (size != -1) 2206 fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size); 2207 code = size; 2208 } 2209 2210 /* 2211 * Get last modification time of file on remote machine 2212 */ 2213 void 2214 modtime(int argc, char *argv[]) 2215 { 2216 time_t mtime; 2217 2218 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 2219 fprintf(ttyout, "usage: %s file\n", argv[0]); 2220 code = -1; 2221 return; 2222 } 2223 mtime = remotemodtime(argv[1], 1); 2224 if (mtime != -1) 2225 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); 2226 code = mtime; 2227 } 2228 2229 /* 2230 * Show status on remote machine 2231 */ 2232 void 2233 rmtstatus(int argc, char *argv[]) 2234 { 2235 2236 (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]); 2237 } 2238 2239 /* 2240 * Get file if modtime is more recent than current file 2241 */ 2242 void 2243 newer(int argc, char *argv[]) 2244 { 2245 2246 if (getit(argc, argv, -1, "w")) 2247 fprintf(ttyout, "Local file \"%s\" is newer than remote file \"%s\".\n", 2248 argv[2], argv[1]); 2249 } 2250 2251 /* 2252 * Display one file through $PAGER (defaults to "more"). 2253 */ 2254 void 2255 page(int argc, char *argv[]) 2256 { 2257 int orestart_point, ohash, overbose; 2258 char *p, *pager, *oldargv1; 2259 2260 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 2261 fprintf(ttyout, "usage: %s file\n", argv[0]); 2262 code = -1; 2263 return; 2264 } 2265 oldargv1 = argv[1]; 2266 if (!globulize(&argv[1])) { 2267 code = -1; 2268 return; 2269 } 2270 p = getenv("PAGER"); 2271 if (p == NULL || (*p == '\0')) 2272 p = PAGER; 2273 if (asprintf(&pager, "|%s", p) == -1) 2274 errx(1, "Can't allocate memory for $PAGER"); 2275 2276 orestart_point = restart_point; 2277 ohash = hash; 2278 overbose = verbose; 2279 restart_point = hash = verbose = 0; 2280 recvrequest("RETR", pager, argv[1], "r+w", 1, 0); 2281 (void)free(pager); 2282 restart_point = orestart_point; 2283 hash = ohash; 2284 verbose = overbose; 2285 if (oldargv1 != argv[1]) /* free up after globulize() */ 2286 free(argv[1]); 2287 } 2288