1 /* $OpenBSD: cmds.c,v 1.67 2008/10/16 23:15:53 martynas 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.67 2008/10/16 23:15:53 martynas 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, "a+w"); 528 } 529 #endif /* !SMALL */ 530 531 void 532 get(int argc, char *argv[]) 533 { 534 535 (void)getit(argc, argv, 0, restart_point ? "a+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 restart_point = (ret < 0) ? 0 : stbuf.st_size; 600 } else { 601 if (ret == 0) { 602 time_t mtime; 603 604 mtime = remotemodtime(argv[1], 0); 605 if (mtime == -1) 606 goto freegetit; 607 if (stbuf.st_mtime >= mtime) { 608 rval = 1; 609 goto freegetit; 610 } 611 } 612 } 613 } 614 #endif /* !SMALL */ 615 616 recvrequest("RETR", argv[2], argv[1], mode, 617 argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc); 618 restart_point = 0; 619 freegetit: 620 if (oldargv2 != globargv2) /* free up after globulize() */ 621 free(globargv2); 622 return (rval); 623 } 624 625 /* XXX - Signal race. */ 626 /* ARGSUSED */ 627 void 628 mabort(int signo) 629 { 630 alarmtimer(0); 631 putc('\n', ttyout); 632 (void)fflush(ttyout); 633 if (mflag && fromatty) 634 if (confirm(mname, NULL)) 635 longjmp(jabort, 1); 636 mflag = 0; 637 longjmp(jabort, 1); 638 } 639 640 /* 641 * Get multiple files. 642 */ 643 void 644 mget(int argc, char *argv[]) 645 { 646 extern int optind, optreset; 647 sig_t oldintr; 648 int ch, xargc = 2; 649 char *cp, localcwd[MAXPATHLEN], *xargv[] = {argv[0], NULL, NULL}; 650 static int restartit = 0; 651 #ifndef SMALL 652 extern char *optarg; 653 const char *errstr; 654 int i = 1; 655 char type = NULL, *dummyargv[] = {argv[0], ".", NULL}; 656 FILE *ftemp = NULL; 657 static int depth = 0, max_depth = 0; 658 #endif /* !SMALL */ 659 660 optind = optreset = 1; 661 662 #ifndef SMALL 663 664 if (depth) 665 depth++; 666 667 while ((ch = getopt(argc, argv, "cd:nr")) != -1) { 668 switch(ch) { 669 case 'c': 670 restartit = 1; 671 break; 672 case 'd': 673 max_depth = strtonum(optarg, 0, INT_MAX, &errstr); 674 if (errstr != NULL) { 675 fprintf(ttyout, "bad depth value, %s: %s\n", 676 errstr, optarg); 677 code = -1; 678 return; 679 } 680 break; 681 case 'n': 682 restartit = -1; 683 break; 684 case 'r': 685 depth = 1; 686 break; 687 default: 688 goto usage; 689 } 690 } 691 #endif /* !SMALL */ 692 693 if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) { 694 usage: 695 fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n", 696 argv[0]); 697 code = -1; 698 return; 699 } 700 701 #ifndef SMALL 702 argv[optind - 1] = argv[0]; 703 argc -= optind - 1; 704 argv += optind - 1; 705 #endif /* !SMALL */ 706 707 mname = argv[0]; 708 mflag = 1; 709 if (getcwd(localcwd, sizeof(localcwd)) == NULL) 710 err(1, "can't get cwd"); 711 712 oldintr = signal(SIGINT, mabort); 713 (void)setjmp(jabort); 714 while ((cp = 715 #ifndef SMALL 716 depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) : 717 #endif /* !SMALL */ 718 remglob(argv, proxy, NULL)) != NULL 719 #ifndef SMALL 720 || (mflag && depth && ++i < argc) 721 #endif /* !SMALL */ 722 ) { 723 #ifndef SMALL 724 if (cp == NULL) 725 continue; 726 #endif /* !SMALL */ 727 if (*cp == '\0') { 728 mflag = 0; 729 continue; 730 } 731 if (!mflag) 732 continue; 733 #ifndef SMALL 734 if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0) 735 continue; 736 #endif /* !SMALL */ 737 if (!fileindir(cp, localcwd)) { 738 fprintf(ttyout, "Skipping non-relative filename `%s'\n", 739 cp); 740 continue; 741 } 742 #ifndef SMALL 743 if (type == 'd' && depth == max_depth) 744 continue; 745 #endif /* !SMALL */ 746 if (confirm(argv[0], cp)) { 747 #ifndef SMALL 748 if (type == 'd') { 749 mkdir(cp, 0755); 750 if (chdir(cp) != 0) { 751 warn("local: %s", cp); 752 continue; 753 } 754 755 xargv[1] = cp; 756 cd(xargc, xargv); 757 if (dirchange != 1) 758 goto out; 759 760 xargv[1] = "*"; 761 mget(xargc, xargv); 762 763 xargv[1] = ".."; 764 cd(xargc, xargv); 765 if (dirchange != 1) { 766 mflag = 0; 767 goto out; 768 } 769 770 out: 771 if (chdir("..") != 0) { 772 warn("local: %s", cp); 773 mflag = 0; 774 } 775 continue; 776 } 777 if (type == 's') 778 /* Currently ignored. */ 779 continue; 780 #endif /* !SMALL */ 781 xargv[1] = cp; 782 (void)getit(xargc, xargv, restartit, 783 (restartit == 1 || restart_point) ? "a+w" : "w"); 784 if (!mflag && fromatty) { 785 if (confirm(argv[0], NULL)) 786 mflag = 1; 787 } 788 } 789 } 790 (void)signal(SIGINT, oldintr); 791 #ifndef SMALL 792 if (depth) 793 depth--; 794 if (depth == 0 || mflag == 0) 795 depth = max_depth = mflag = restartit = 0; 796 #else /* !SMALL */ 797 mflag = 0; 798 #endif /* !SMALL */ 799 } 800 801 char * 802 onoff(int bool) 803 { 804 805 return (bool ? "on" : "off"); 806 } 807 808 /* 809 * Show status. 810 */ 811 /*ARGSUSED*/ 812 void 813 status(int argc, char *argv[]) 814 { 815 int i; 816 817 if (connected) 818 fprintf(ttyout, "Connected %sto %s.\n", 819 connected == -1 ? "and logged in" : "", hostname); 820 else 821 fputs("Not connected.\n", ttyout); 822 if (!proxy) { 823 pswitch(1); 824 if (connected) { 825 fprintf(ttyout, "Connected for proxy commands to %s.\n", 826 hostname); 827 } 828 else { 829 fputs("No proxy connection.\n", ttyout); 830 } 831 pswitch(0); 832 } 833 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), 834 *gateserver ? gateserver : "(none)", gateport); 835 fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode)); 836 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", 837 modename, typename, formname, structname); 838 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", 839 onoff(verbose), onoff(bell), onoff(interactive), 840 onoff(doglob)); 841 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", onoff(sunique), 842 onoff(runique)); 843 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); 844 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag)); 845 if (ntflag) { 846 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); 847 } 848 else { 849 fputs("Ntrans: off.\n", ttyout); 850 } 851 if (mapflag) { 852 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); 853 } 854 else { 855 fputs("Nmap: off.\n", ttyout); 856 } 857 fprintf(ttyout, "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", 858 onoff(hash), mark, onoff(progress)); 859 fprintf(ttyout, "Use of PORT/LPRT cmds: %s.\n", onoff(sendport)); 860 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), 861 epsv4bad ? " (disabled for this connection)" : ""); 862 #ifndef SMALL 863 fprintf(ttyout, "Command line editing: %s.\n", onoff(editing)); 864 #endif /* !SMALL */ 865 if (macnum > 0) { 866 fputs("Macros:\n", ttyout); 867 for (i=0; i<macnum; i++) { 868 fprintf(ttyout, "\t%s\n", macros[i].mac_name); 869 } 870 } 871 code = 0; 872 } 873 874 /* 875 * Toggle a variable 876 */ 877 int 878 togglevar(int argc, char *argv[], int *var, const char *mesg) 879 { 880 if (argc < 2) { 881 *var = !*var; 882 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { 883 *var = 1; 884 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { 885 *var = 0; 886 } else { 887 fprintf(ttyout, "usage: %s [on | off]\n", argv[0]); 888 return (-1); 889 } 890 if (mesg) 891 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); 892 return (*var); 893 } 894 895 /* 896 * Set beep on cmd completed mode. 897 */ 898 /*ARGSUSED*/ 899 void 900 setbell(int argc, char *argv[]) 901 { 902 903 code = togglevar(argc, argv, &bell, "Bell mode"); 904 } 905 906 /* 907 * Set command line editing 908 */ 909 #ifndef SMALL 910 /*ARGSUSED*/ 911 void 912 setedit(int argc, char *argv[]) 913 { 914 915 code = togglevar(argc, argv, &editing, "Editing mode"); 916 controlediting(); 917 } 918 #endif /* !SMALL */ 919 920 /* 921 * Toggle use of IPv4 EPSV/EPRT 922 */ 923 /*ARGSUSED*/ 924 void 925 setepsv4(int argc, char *argv[]) 926 { 927 928 code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4"); 929 epsv4bad = 0; 930 } 931 932 /* 933 * Turn on packet tracing. 934 */ 935 /*ARGSUSED*/ 936 void 937 settrace(int argc, char *argv[]) 938 { 939 940 code = togglevar(argc, argv, &trace, "Packet tracing"); 941 } 942 943 /* 944 * Toggle hash mark printing during transfers, or set hash mark bytecount. 945 */ 946 /*ARGSUSED*/ 947 void 948 sethash(int argc, char *argv[]) 949 { 950 if (argc == 1) 951 hash = !hash; 952 else if (argc != 2) { 953 fprintf(ttyout, "usage: %s [on | off | size]\n", argv[0]); 954 code = -1; 955 return; 956 } else if (strcasecmp(argv[1], "on") == 0) 957 hash = 1; 958 else if (strcasecmp(argv[1], "off") == 0) 959 hash = 0; 960 else { 961 int nmark; 962 const char *errstr; 963 964 nmark = strtonum(argv[1], 1, INT_MAX, &errstr); 965 if (errstr) { 966 fprintf(ttyout, "bytecount value is %s: %s\n", 967 errstr, argv[1]); 968 code = -1; 969 return; 970 } 971 mark = nmark; 972 hash = 1; 973 } 974 fprintf(ttyout, "Hash mark printing %s", onoff(hash)); 975 if (hash) 976 fprintf(ttyout, " (%d bytes/hash mark)", mark); 977 fputs(".\n", ttyout); 978 code = hash; 979 } 980 981 /* 982 * Turn on printing of server echo's. 983 */ 984 /*ARGSUSED*/ 985 void 986 setverbose(int argc, char *argv[]) 987 { 988 989 code = togglevar(argc, argv, &verbose, "Verbose mode"); 990 } 991 992 /* 993 * Toggle PORT/LPRT cmd use before each data connection. 994 */ 995 /*ARGSUSED*/ 996 void 997 setport(int argc, char *argv[]) 998 { 999 1000 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); 1001 } 1002 1003 /* 1004 * Toggle transfer progress bar. 1005 */ 1006 /*ARGSUSED*/ 1007 void 1008 setprogress(int argc, char *argv[]) 1009 { 1010 1011 code = togglevar(argc, argv, &progress, "Progress bar"); 1012 } 1013 1014 /* 1015 * Turn on interactive prompting during mget, mput, and mdelete. 1016 */ 1017 /*ARGSUSED*/ 1018 void 1019 setprompt(int argc, char *argv[]) 1020 { 1021 1022 code = togglevar(argc, argv, &interactive, "Interactive mode"); 1023 } 1024 1025 /* 1026 * Toggle gate-ftp mode, or set gate-ftp server 1027 */ 1028 /*ARGSUSED*/ 1029 void 1030 setgate(int argc, char *argv[]) 1031 { 1032 static char gsbuf[MAXHOSTNAMELEN]; 1033 1034 if (argc > 3) { 1035 fprintf(ttyout, "usage: %s [on | off | host [port]]\n", 1036 argv[0]); 1037 code = -1; 1038 return; 1039 } else if (argc < 2) { 1040 gatemode = !gatemode; 1041 } else { 1042 if (argc == 2 && strcasecmp(argv[1], "on") == 0) 1043 gatemode = 1; 1044 else if (argc == 2 && strcasecmp(argv[1], "off") == 0) 1045 gatemode = 0; 1046 else { 1047 if (argc == 3) { 1048 gateport = strdup(argv[2]); 1049 if (gateport == NULL) 1050 err(1, NULL); 1051 } 1052 strlcpy(gsbuf, argv[1], sizeof(gsbuf)); 1053 gateserver = gsbuf; 1054 gatemode = 1; 1055 } 1056 } 1057 if (gatemode && (gateserver == NULL || *gateserver == '\0')) { 1058 fprintf(ttyout, 1059 "Disabling gate-ftp mode - no gate-ftp server defined.\n"); 1060 gatemode = 0; 1061 } else { 1062 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", 1063 onoff(gatemode), 1064 *gateserver ? gateserver : "(none)", gateport); 1065 } 1066 code = gatemode; 1067 } 1068 1069 /* 1070 * Toggle metacharacter interpretation on local file names. 1071 */ 1072 /*ARGSUSED*/ 1073 void 1074 setglob(int argc, char *argv[]) 1075 { 1076 1077 code = togglevar(argc, argv, &doglob, "Globbing"); 1078 } 1079 1080 /* 1081 * Toggle preserving modification times on retrieved files. 1082 */ 1083 /*ARGSUSED*/ 1084 void 1085 setpreserve(int argc, char *argv[]) 1086 { 1087 1088 code = togglevar(argc, argv, &preserve, "Preserve modification times"); 1089 } 1090 1091 /* 1092 * Set debugging mode on/off and/or set level of debugging. 1093 */ 1094 #ifndef SMALL 1095 /*ARGSUSED*/ 1096 void 1097 setdebug(int argc, char *argv[]) 1098 { 1099 if (argc > 2) { 1100 fprintf(ttyout, "usage: %s [on | off | debuglevel]\n", argv[0]); 1101 code = -1; 1102 return; 1103 } else if (argc == 2) { 1104 if (strcasecmp(argv[1], "on") == 0) 1105 debug = 1; 1106 else if (strcasecmp(argv[1], "off") == 0) 1107 debug = 0; 1108 else { 1109 const char *errstr; 1110 int val; 1111 1112 val = strtonum(argv[1], 0, INT_MAX, &errstr); 1113 if (errstr) { 1114 fprintf(ttyout, "debugging value is %s: %s\n", 1115 errstr, argv[1]); 1116 code = -1; 1117 return; 1118 } 1119 debug = val; 1120 } 1121 } else 1122 debug = !debug; 1123 if (debug) 1124 options |= SO_DEBUG; 1125 else 1126 options &= ~SO_DEBUG; 1127 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug); 1128 code = debug > 0; 1129 } 1130 #endif /* !SMALL */ 1131 1132 /* 1133 * Set current working directory on remote machine. 1134 */ 1135 void 1136 cd(int argc, char *argv[]) 1137 { 1138 int r; 1139 1140 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) || 1141 argc > 2) { 1142 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); 1143 code = -1; 1144 return; 1145 } 1146 r = command("CWD %s", argv[1]); 1147 if (r == ERROR && code == 500) { 1148 if (verbose) 1149 fputs("CWD command not recognized, trying XCWD.\n", ttyout); 1150 r = command("XCWD %s", argv[1]); 1151 } 1152 if (r == ERROR && code == 550) { 1153 dirchange = 0; 1154 return; 1155 } 1156 if (r == COMPLETE) 1157 dirchange = 1; 1158 } 1159 1160 /* 1161 * Set current working directory on local machine. 1162 */ 1163 void 1164 lcd(int argc, char *argv[]) 1165 { 1166 char buf[MAXPATHLEN]; 1167 char *oldargv1; 1168 1169 if (argc < 2) 1170 argc++, argv[1] = home; 1171 if (argc != 2) { 1172 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]); 1173 code = -1; 1174 return; 1175 } 1176 oldargv1 = argv[1]; 1177 if (!globulize(&argv[1])) { 1178 code = -1; 1179 return; 1180 } 1181 if (chdir(argv[1]) < 0) { 1182 warn("local: %s", argv[1]); 1183 code = -1; 1184 } else { 1185 if (getcwd(buf, sizeof(buf)) != NULL) 1186 fprintf(ttyout, "Local directory now %s\n", buf); 1187 else 1188 warn("getcwd: %s", argv[1]); 1189 code = 0; 1190 } 1191 if (oldargv1 != argv[1]) /* free up after globulize() */ 1192 free(argv[1]); 1193 } 1194 1195 /* 1196 * Delete a single file. 1197 */ 1198 void 1199 deletecmd(int argc, char *argv[]) 1200 { 1201 1202 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) { 1203 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 1204 code = -1; 1205 return; 1206 } 1207 (void)command("DELE %s", argv[1]); 1208 } 1209 1210 /* 1211 * Delete multiple files. 1212 */ 1213 void 1214 mdelete(int argc, char *argv[]) 1215 { 1216 sig_t oldintr; 1217 char *cp; 1218 1219 if (argc < 2 && !another(&argc, &argv, "remote-files")) { 1220 fprintf(ttyout, "usage: %s remote-files\n", argv[0]); 1221 code = -1; 1222 return; 1223 } 1224 mname = argv[0]; 1225 mflag = 1; 1226 oldintr = signal(SIGINT, mabort); 1227 (void)setjmp(jabort); 1228 while ((cp = remglob(argv, 0, NULL)) != NULL) { 1229 if (*cp == '\0') { 1230 mflag = 0; 1231 continue; 1232 } 1233 if (mflag && confirm(argv[0], cp)) { 1234 (void)command("DELE %s", cp); 1235 if (!mflag && fromatty) { 1236 if (confirm(argv[0], NULL)) 1237 mflag = 1; 1238 } 1239 } 1240 } 1241 (void)signal(SIGINT, oldintr); 1242 mflag = 0; 1243 } 1244 1245 /* 1246 * Rename a remote file. 1247 */ 1248 void 1249 renamefile(int argc, char *argv[]) 1250 { 1251 1252 if (argc < 2 && !another(&argc, &argv, "from-name")) 1253 goto usage; 1254 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { 1255 usage: 1256 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]); 1257 code = -1; 1258 return; 1259 } 1260 if (command("RNFR %s", argv[1]) == CONTINUE) 1261 (void)command("RNTO %s", argv[2]); 1262 } 1263 1264 /* 1265 * Get a directory listing of remote files. 1266 */ 1267 void 1268 ls(int argc, char *argv[]) 1269 { 1270 const char *cmd; 1271 char *oldargv2, *globargv2; 1272 1273 if (argc < 2) 1274 argc++, argv[1] = NULL; 1275 if (argc < 3) 1276 argc++, argv[2] = "-"; 1277 if (argc > 3) { 1278 fprintf(ttyout, "usage: %s [remote-directory [local-file]]\n", 1279 argv[0]); 1280 code = -1; 1281 return; 1282 } 1283 cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST"; 1284 oldargv2 = argv[2]; 1285 if (strcmp(argv[2], "-") && !globulize(&argv[2])) { 1286 code = -1; 1287 return; 1288 } 1289 globargv2 = argv[2]; 1290 if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) || 1291 !confirm("output to local-file:", argv[2]))) { 1292 code = -1; 1293 goto freels; 1294 } 1295 recvrequest(cmd, argv[2], argv[1], "w", 0, 0); 1296 1297 /* flush results in case commands are coming from a pipe */ 1298 fflush(ttyout); 1299 freels: 1300 if (argv[2] != globargv2) /* free up after globulize() */ 1301 free(argv[2]); 1302 if (globargv2 != oldargv2) 1303 free(globargv2); 1304 } 1305 1306 /* 1307 * Get a directory listing of multiple remote files. 1308 */ 1309 void 1310 mls(int argc, char *argv[]) 1311 { 1312 sig_t oldintr; 1313 int i; 1314 char lmode[1], *dest, *odest; 1315 1316 if (argc < 2 && !another(&argc, &argv, "remote-files")) 1317 goto usage; 1318 if (argc < 3 && !another(&argc, &argv, "local-file")) { 1319 usage: 1320 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]); 1321 code = -1; 1322 return; 1323 } 1324 odest = dest = argv[argc - 1]; 1325 argv[argc - 1] = NULL; 1326 if (strcmp(dest, "-") && *dest != '|') 1327 if (!globulize(&dest) || 1328 !confirm("output to local-file:", dest)) { 1329 code = -1; 1330 return; 1331 } 1332 mname = argv[0]; 1333 mflag = 1; 1334 oldintr = signal(SIGINT, mabort); 1335 (void)setjmp(jabort); 1336 for (i = 1; mflag && i < argc-1; ++i) { 1337 *lmode = (i == 1) ? 'w' : 'a'; 1338 recvrequest("LIST", dest, argv[i], lmode, 0, 0); 1339 if (!mflag && fromatty) { 1340 if (confirm(argv[0], NULL)) 1341 mflag ++; 1342 } 1343 } 1344 (void)signal(SIGINT, oldintr); 1345 mflag = 0; 1346 if (dest != odest) /* free up after globulize() */ 1347 free(dest); 1348 } 1349 1350 /* 1351 * Do a shell escape 1352 */ 1353 /*ARGSUSED*/ 1354 void 1355 shell(int argc, char *argv[]) 1356 { 1357 pid_t pid; 1358 sig_t old1, old2; 1359 char shellnam[MAXPATHLEN], *shellp, *namep; 1360 int wait_status; 1361 1362 old1 = signal (SIGINT, SIG_IGN); 1363 old2 = signal (SIGQUIT, SIG_IGN); 1364 if ((pid = fork()) == 0) { 1365 for (pid = 3; pid < 20; pid++) 1366 (void)close(pid); 1367 (void)signal(SIGINT, SIG_DFL); 1368 (void)signal(SIGQUIT, SIG_DFL); 1369 shellp = getenv("SHELL"); 1370 if (shellp == NULL || *shellp == '\0') 1371 shellp = _PATH_BSHELL; 1372 namep = strrchr(shellp, '/'); 1373 if (namep == NULL) 1374 namep = shellp; 1375 shellnam[0] = '-'; 1376 (void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1); 1377 if (strcmp(namep, "sh") != 0) 1378 shellnam[0] = '+'; 1379 #ifndef SMALL 1380 if (debug) { 1381 fputs(shellp, ttyout); 1382 fputc('\n', ttyout); 1383 (void)fflush(ttyout); 1384 } 1385 #endif /* !SMALL */ 1386 if (argc > 1) { 1387 execl(shellp, shellnam, "-c", altarg, (char *)0); 1388 } 1389 else { 1390 execl(shellp, shellnam, (char *)0); 1391 } 1392 warn("%s", shellp); 1393 code = -1; 1394 exit(1); 1395 } 1396 if (pid > 0) 1397 while (wait(&wait_status) != pid) 1398 ; 1399 (void)signal(SIGINT, old1); 1400 (void)signal(SIGQUIT, old2); 1401 if (pid == -1) { 1402 warn("Try again later"); 1403 code = -1; 1404 } 1405 else { 1406 code = 0; 1407 } 1408 } 1409 1410 /* 1411 * Send new user information (re-login) 1412 */ 1413 void 1414 user(int argc, char *argv[]) 1415 { 1416 char acctname[80]; 1417 int n, aflag = 0; 1418 1419 if (argc < 2) 1420 (void)another(&argc, &argv, "username"); 1421 if (argc < 2 || argc > 4) { 1422 fprintf(ttyout, "usage: %s username [password [account]]\n", 1423 argv[0]); 1424 code = -1; 1425 return; 1426 } 1427 n = command("USER %s", argv[1]); 1428 if (n == CONTINUE) { 1429 if (argc < 3 ) 1430 argv[2] = getpass("Password:"), argc++; 1431 n = command("PASS %s", argv[2]); 1432 } 1433 if (n == CONTINUE) { 1434 if (argc < 4) { 1435 (void)fputs("Account: ", ttyout); 1436 (void)fflush(ttyout); 1437 if (fgets(acctname, sizeof(acctname), stdin) == NULL) { 1438 clearerr(stdin); 1439 goto fail; 1440 } 1441 1442 acctname[strcspn(acctname, "\n")] = '\0'; 1443 1444 argv[3] = acctname; 1445 argc++; 1446 } 1447 n = command("ACCT %s", argv[3]); 1448 aflag++; 1449 } 1450 if (n != COMPLETE) { 1451 fail: 1452 fputs("Login failed.\n", ttyout); 1453 return; 1454 } 1455 if (!aflag && argc == 4) { 1456 (void)command("ACCT %s", argv[3]); 1457 } 1458 connected = -1; 1459 } 1460 1461 /* 1462 * Print working directory on remote machine. 1463 */ 1464 /*ARGSUSED*/ 1465 void 1466 pwd(int argc, char *argv[]) 1467 { 1468 int oldverbose = verbose; 1469 1470 /* 1471 * If we aren't verbose, this doesn't do anything! 1472 */ 1473 verbose = 1; 1474 if (command("PWD") == ERROR && code == 500) { 1475 fputs("PWD command not recognized, trying XPWD.\n", ttyout); 1476 (void)command("XPWD"); 1477 } 1478 verbose = oldverbose; 1479 } 1480 1481 /* 1482 * Print working directory on local machine. 1483 */ 1484 /* ARGSUSED */ 1485 void 1486 lpwd(int argc, char *argv[]) 1487 { 1488 char buf[MAXPATHLEN]; 1489 1490 if (getcwd(buf, sizeof(buf)) != NULL) 1491 fprintf(ttyout, "Local directory %s\n", buf); 1492 else 1493 warn("getcwd"); 1494 code = 0; 1495 } 1496 1497 /* 1498 * Make a directory. 1499 */ 1500 void 1501 makedir(int argc, char *argv[]) 1502 { 1503 1504 if ((argc < 2 && !another(&argc, &argv, "directory-name")) || 1505 argc > 2) { 1506 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1507 code = -1; 1508 return; 1509 } 1510 if (command("MKD %s", argv[1]) == ERROR && code == 500) { 1511 if (verbose) 1512 fputs("MKD command not recognized, trying XMKD.\n", ttyout); 1513 (void)command("XMKD %s", argv[1]); 1514 } 1515 } 1516 1517 /* 1518 * Remove a directory. 1519 */ 1520 void 1521 removedir(int argc, char *argv[]) 1522 { 1523 1524 if ((argc < 2 && !another(&argc, &argv, "directory-name")) || 1525 argc > 2) { 1526 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1527 code = -1; 1528 return; 1529 } 1530 if (command("RMD %s", argv[1]) == ERROR && code == 500) { 1531 if (verbose) 1532 fputs("RMD command not recognized, trying XRMD.\n", ttyout); 1533 (void)command("XRMD %s", argv[1]); 1534 } 1535 } 1536 1537 /* 1538 * Send a line, verbatim, to the remote machine. 1539 */ 1540 void 1541 quote(int argc, char *argv[]) 1542 { 1543 1544 if (argc < 2 && !another(&argc, &argv, "command line to send")) { 1545 fprintf(ttyout, "usage: %s arg ...\n", argv[0]); 1546 code = -1; 1547 return; 1548 } 1549 quote1("", argc, argv); 1550 } 1551 1552 /* 1553 * Send a SITE command to the remote machine. The line 1554 * is sent verbatim to the remote machine, except that the 1555 * word "SITE" is added at the front. 1556 */ 1557 void 1558 site(int argc, char *argv[]) 1559 { 1560 1561 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) { 1562 fprintf(ttyout, "usage: %s arg ...\n", argv[0]); 1563 code = -1; 1564 return; 1565 } 1566 quote1("SITE", argc, argv); 1567 } 1568 1569 /* 1570 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1571 * Send the result as a one-line command and get response. 1572 */ 1573 void 1574 quote1(const char *initial, int argc, char *argv[]) 1575 { 1576 int i, len; 1577 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1578 1579 (void)strlcpy(buf, initial, sizeof(buf)); 1580 if (argc > 1) { 1581 for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) { 1582 /* Space for next arg */ 1583 if (len > 1) 1584 buf[len++] = ' '; 1585 1586 /* Sanity check */ 1587 if (len >= sizeof(buf) - 1) 1588 break; 1589 1590 /* Copy next argument, NUL terminate always */ 1591 strlcpy(&buf[len], argv[i], sizeof(buf) - len); 1592 1593 /* Update string length */ 1594 len = strlen(buf); 1595 } 1596 } 1597 1598 /* Make double (triple?) sure the sucker is NUL terminated */ 1599 buf[sizeof(buf) - 1] = '\0'; 1600 1601 if (command("%s", buf) == PRELIM) { 1602 while (getreply(0) == PRELIM) 1603 continue; 1604 } 1605 } 1606 1607 void 1608 do_chmod(int argc, char *argv[]) 1609 { 1610 1611 if (argc < 2 && !another(&argc, &argv, "mode")) 1612 goto usage; 1613 if ((argc < 3 && !another(&argc, &argv, "file")) || argc > 3) { 1614 usage: 1615 fprintf(ttyout, "usage: %s mode file\n", argv[0]); 1616 code = -1; 1617 return; 1618 } 1619 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1620 } 1621 1622 void 1623 do_umask(int argc, char *argv[]) 1624 { 1625 int oldverbose = verbose; 1626 1627 verbose = 1; 1628 (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); 1629 verbose = oldverbose; 1630 } 1631 1632 void 1633 idle(int argc, char *argv[]) 1634 { 1635 int oldverbose = verbose; 1636 1637 verbose = 1; 1638 (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); 1639 verbose = oldverbose; 1640 } 1641 1642 /* 1643 * Ask the other side for help. 1644 */ 1645 void 1646 rmthelp(int argc, char *argv[]) 1647 { 1648 int oldverbose = verbose; 1649 1650 verbose = 1; 1651 (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]); 1652 verbose = oldverbose; 1653 } 1654 1655 /* 1656 * Terminate session and exit. 1657 */ 1658 /*ARGSUSED*/ 1659 void 1660 quit(int argc, char *argv[]) 1661 { 1662 1663 if (connected) 1664 disconnect(0, 0); 1665 pswitch(1); 1666 if (connected) { 1667 disconnect(0, 0); 1668 } 1669 exit(0); 1670 } 1671 1672 /* 1673 * Terminate session, but don't exit. 1674 */ 1675 /* ARGSUSED */ 1676 void 1677 disconnect(int argc, char *argv[]) 1678 { 1679 1680 if (!connected) 1681 return; 1682 (void)command("QUIT"); 1683 if (cout) { 1684 (void)fclose(cout); 1685 } 1686 cout = NULL; 1687 connected = 0; 1688 data = -1; 1689 if (!proxy) { 1690 macnum = 0; 1691 } 1692 } 1693 1694 void 1695 account(int argc, char *argv[]) 1696 { 1697 char *ap; 1698 1699 if (argc > 2) { 1700 fprintf(ttyout, "usage: %s [password]\n", argv[0]); 1701 code = -1; 1702 return; 1703 } 1704 else if (argc == 2) 1705 ap = argv[1]; 1706 else 1707 ap = getpass("Account:"); 1708 (void)command("ACCT %s", ap); 1709 } 1710 1711 jmp_buf abortprox; 1712 1713 /* ARGSUSED */ 1714 void 1715 proxabort(int signo) 1716 { 1717 1718 alarmtimer(0); 1719 if (!proxy) { 1720 pswitch(1); 1721 } 1722 if (connected) { 1723 proxflag = 1; 1724 } 1725 else { 1726 proxflag = 0; 1727 } 1728 pswitch(0); 1729 longjmp(abortprox, 1); 1730 } 1731 1732 void 1733 doproxy(int argc, char *argv[]) 1734 { 1735 struct cmd *c; 1736 int cmdpos; 1737 sig_t oldintr; 1738 1739 if (argc < 2 && !another(&argc, &argv, "command")) { 1740 fprintf(ttyout, "usage: %s command\n", argv[0]); 1741 code = -1; 1742 return; 1743 } 1744 c = getcmd(argv[1]); 1745 if (c == (struct cmd *) -1) { 1746 fputs("?Ambiguous command.\n", ttyout); 1747 (void)fflush(ttyout); 1748 code = -1; 1749 return; 1750 } 1751 if (c == 0) { 1752 fputs("?Invalid command.\n", ttyout); 1753 (void)fflush(ttyout); 1754 code = -1; 1755 return; 1756 } 1757 if (!c->c_proxy) { 1758 fputs("?Invalid proxy command.\n", ttyout); 1759 (void)fflush(ttyout); 1760 code = -1; 1761 return; 1762 } 1763 if (setjmp(abortprox)) { 1764 code = -1; 1765 return; 1766 } 1767 oldintr = signal(SIGINT, proxabort); 1768 pswitch(1); 1769 if (c->c_conn && !connected) { 1770 fputs("Not connected.\n", ttyout); 1771 (void)fflush(ttyout); 1772 pswitch(0); 1773 (void)signal(SIGINT, oldintr); 1774 code = -1; 1775 return; 1776 } 1777 cmdpos = strcspn(line, " \t"); 1778 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1779 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1780 (*c->c_handler)(argc-1, argv+1); 1781 if (connected) { 1782 proxflag = 1; 1783 } 1784 else { 1785 proxflag = 0; 1786 } 1787 pswitch(0); 1788 (void)signal(SIGINT, oldintr); 1789 } 1790 1791 void 1792 setcase(int argc, char *argv[]) 1793 { 1794 1795 code = togglevar(argc, argv, &mcase, "Case mapping"); 1796 } 1797 1798 void 1799 setcr(int argc, char *argv[]) 1800 { 1801 1802 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1803 } 1804 1805 void 1806 setntrans(int argc, char *argv[]) 1807 { 1808 if (argc == 1) { 1809 ntflag = 0; 1810 fputs("Ntrans off.\n", ttyout); 1811 code = ntflag; 1812 return; 1813 } 1814 ntflag++; 1815 code = ntflag; 1816 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1817 if (argc == 2) { 1818 ntout[0] = '\0'; 1819 return; 1820 } 1821 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1822 } 1823 1824 char * 1825 dotrans(char *name) 1826 { 1827 static char new[MAXPATHLEN]; 1828 char *cp1, *cp2 = new; 1829 int i, ostop, found; 1830 1831 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 1832 continue; 1833 for (cp1 = name; *cp1; cp1++) { 1834 found = 0; 1835 for (i = 0; *(ntin + i) && i < 16; i++) { 1836 if (*cp1 == *(ntin + i)) { 1837 found++; 1838 if (i < ostop) { 1839 *cp2++ = *(ntout + i); 1840 } 1841 break; 1842 } 1843 } 1844 if (!found) { 1845 *cp2++ = *cp1; 1846 } 1847 } 1848 *cp2 = '\0'; 1849 return (new); 1850 } 1851 1852 void 1853 setnmap(int argc, char *argv[]) 1854 { 1855 char *cp; 1856 1857 if (argc == 1) { 1858 mapflag = 0; 1859 fputs("Nmap off.\n", ttyout); 1860 code = mapflag; 1861 return; 1862 } 1863 if ((argc < 3 && !another(&argc, &argv, "outpattern")) || argc > 3) { 1864 fprintf(ttyout, "usage: %s [inpattern outpattern]\n", argv[0]); 1865 code = -1; 1866 return; 1867 } 1868 mapflag = 1; 1869 code = 1; 1870 cp = strchr(altarg, ' '); 1871 if (proxy) { 1872 while(*++cp == ' ') 1873 continue; 1874 altarg = cp; 1875 cp = strchr(altarg, ' '); 1876 } 1877 *cp = '\0'; 1878 (void)strncpy(mapin, altarg, MAXPATHLEN - 1); 1879 while (*++cp == ' ') 1880 continue; 1881 (void)strncpy(mapout, cp, MAXPATHLEN - 1); 1882 } 1883 1884 char * 1885 domap(char *name) 1886 { 1887 static char new[MAXPATHLEN]; 1888 char *cp1 = name, *cp2 = mapin; 1889 char *tp[9], *te[9]; 1890 int i, toks[9], toknum = 0, match = 1; 1891 1892 for (i=0; i < 9; ++i) { 1893 toks[i] = 0; 1894 } 1895 while (match && *cp1 && *cp2) { 1896 switch (*cp2) { 1897 case '\\': 1898 if (*++cp2 != *cp1) { 1899 match = 0; 1900 } 1901 break; 1902 case '$': 1903 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 1904 if (*cp1 != *(++cp2+1)) { 1905 toks[toknum = *cp2 - '1']++; 1906 tp[toknum] = cp1; 1907 while (*++cp1 && *(cp2+1) 1908 != *cp1); 1909 te[toknum] = cp1; 1910 } 1911 cp2++; 1912 break; 1913 } 1914 /* FALLTHROUGH */ 1915 default: 1916 if (*cp2 != *cp1) { 1917 match = 0; 1918 } 1919 break; 1920 } 1921 if (match && *cp1) { 1922 cp1++; 1923 } 1924 if (match && *cp2) { 1925 cp2++; 1926 } 1927 } 1928 if (!match && *cp1) /* last token mismatch */ 1929 { 1930 toks[toknum] = 0; 1931 } 1932 cp1 = new; 1933 *cp1 = '\0'; 1934 cp2 = mapout; 1935 while (*cp2) { 1936 match = 0; 1937 switch (*cp2) { 1938 case '\\': 1939 if (*(cp2 + 1)) { 1940 *cp1++ = *++cp2; 1941 } 1942 break; 1943 case '[': 1944 LOOP: 1945 if (*++cp2 == '$' && isdigit(*(cp2+1))) { 1946 if (*++cp2 == '0') { 1947 char *cp3 = name; 1948 1949 while (*cp3) { 1950 *cp1++ = *cp3++; 1951 } 1952 match = 1; 1953 } 1954 else if (toks[toknum = *cp2 - '1']) { 1955 char *cp3 = tp[toknum]; 1956 1957 while (cp3 != te[toknum]) { 1958 *cp1++ = *cp3++; 1959 } 1960 match = 1; 1961 } 1962 } 1963 else { 1964 while (*cp2 && *cp2 != ',' && 1965 *cp2 != ']') { 1966 if (*cp2 == '\\') { 1967 cp2++; 1968 } 1969 else if (*cp2 == '$' && 1970 isdigit(*(cp2+1))) { 1971 if (*++cp2 == '0') { 1972 char *cp3 = name; 1973 1974 while (*cp3) { 1975 *cp1++ = *cp3++; 1976 } 1977 } 1978 else if (toks[toknum = 1979 *cp2 - '1']) { 1980 char *cp3=tp[toknum]; 1981 1982 while (cp3 != 1983 te[toknum]) { 1984 *cp1++ = *cp3++; 1985 } 1986 } 1987 } 1988 else if (*cp2) { 1989 *cp1++ = *cp2++; 1990 } 1991 } 1992 if (!*cp2) { 1993 fputs( 1994 "nmap: unbalanced brackets.\n", ttyout); 1995 return (name); 1996 } 1997 match = 1; 1998 cp2--; 1999 } 2000 if (match) { 2001 while (*++cp2 && *cp2 != ']') { 2002 if (*cp2 == '\\' && *(cp2 + 1)) { 2003 cp2++; 2004 } 2005 } 2006 if (!*cp2) { 2007 fputs( 2008 "nmap: unbalanced brackets.\n", ttyout); 2009 return (name); 2010 } 2011 break; 2012 } 2013 switch (*++cp2) { 2014 case ',': 2015 goto LOOP; 2016 case ']': 2017 break; 2018 default: 2019 cp2--; 2020 goto LOOP; 2021 } 2022 break; 2023 case '$': 2024 if (isdigit(*(cp2 + 1))) { 2025 if (*++cp2 == '0') { 2026 char *cp3 = name; 2027 2028 while (*cp3) { 2029 *cp1++ = *cp3++; 2030 } 2031 } 2032 else if (toks[toknum = *cp2 - '1']) { 2033 char *cp3 = tp[toknum]; 2034 2035 while (cp3 != te[toknum]) { 2036 *cp1++ = *cp3++; 2037 } 2038 } 2039 break; 2040 } 2041 /* FALLTHROUGH */ 2042 default: 2043 *cp1++ = *cp2; 2044 break; 2045 } 2046 cp2++; 2047 } 2048 *cp1 = '\0'; 2049 if (!*new) { 2050 return (name); 2051 } 2052 return (new); 2053 } 2054 2055 void 2056 setpassive(int argc, char *argv[]) 2057 { 2058 2059 code = togglevar(argc, argv, &passivemode, 2060 verbose ? "Passive mode" : NULL); 2061 } 2062 2063 void 2064 setsunique(int argc, char *argv[]) 2065 { 2066 2067 code = togglevar(argc, argv, &sunique, "Store unique"); 2068 } 2069 2070 void 2071 setrunique(int argc, char *argv[]) 2072 { 2073 2074 code = togglevar(argc, argv, &runique, "Receive unique"); 2075 } 2076 2077 /* change directory to parent directory */ 2078 /* ARGSUSED */ 2079 void 2080 cdup(int argc, char *argv[]) 2081 { 2082 int r; 2083 2084 r = command("CDUP"); 2085 if (r == ERROR && code == 500) { 2086 if (verbose) 2087 fputs("CDUP command not recognized, trying XCUP.\n", ttyout); 2088 r = command("XCUP"); 2089 } 2090 if (r == COMPLETE) 2091 dirchange = 1; 2092 } 2093 2094 /* 2095 * Restart transfer at specific point 2096 */ 2097 void 2098 restart(int argc, char *argv[]) 2099 { 2100 quad_t nrestart_point; 2101 char *ep; 2102 2103 if (argc != 2) 2104 fputs("restart: offset not specified.\n", ttyout); 2105 else { 2106 nrestart_point = strtoq(argv[1], &ep, 10); 2107 if (nrestart_point == QUAD_MAX || *ep != '\0') 2108 fputs("restart: invalid offset.\n", ttyout); 2109 else { 2110 fprintf(ttyout, "Restarting at %lld. Execute get, put " 2111 "or append to initiate transfer\n", 2112 (long long)nrestart_point); 2113 restart_point = nrestart_point; 2114 } 2115 } 2116 } 2117 2118 /* 2119 * Show remote system type 2120 */ 2121 /* ARGSUSED */ 2122 void 2123 syst(int argc, char *argv[]) 2124 { 2125 2126 (void)command("SYST"); 2127 } 2128 2129 void 2130 macdef(int argc, char *argv[]) 2131 { 2132 char *tmp; 2133 int c; 2134 2135 if (macnum == 16) { 2136 fputs("Limit of 16 macros have already been defined.\n", ttyout); 2137 code = -1; 2138 return; 2139 } 2140 if ((argc < 2 && !another(&argc, &argv, "macro-name")) || argc > 2) { 2141 fprintf(ttyout, "usage: %s macro-name\n", argv[0]); 2142 code = -1; 2143 return; 2144 } 2145 if (interactive) 2146 fputs( 2147 "Enter macro line by line, terminating it with a null line.\n", ttyout); 2148 (void)strlcpy(macros[macnum].mac_name, argv[1], 2149 sizeof(macros[macnum].mac_name)); 2150 if (macnum == 0) 2151 macros[macnum].mac_start = macbuf; 2152 else 2153 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2154 tmp = macros[macnum].mac_start; 2155 while (tmp != macbuf+4096) { 2156 if ((c = getchar()) == EOF) { 2157 fputs("macdef: end of file encountered.\n", ttyout); 2158 code = -1; 2159 return; 2160 } 2161 if ((*tmp = c) == '\n') { 2162 if (tmp == macros[macnum].mac_start) { 2163 macros[macnum++].mac_end = tmp; 2164 code = 0; 2165 return; 2166 } 2167 if (*(tmp-1) == '\0') { 2168 macros[macnum++].mac_end = tmp - 1; 2169 code = 0; 2170 return; 2171 } 2172 *tmp = '\0'; 2173 } 2174 tmp++; 2175 } 2176 while (1) { 2177 while ((c = getchar()) != '\n' && c != EOF) 2178 /* LOOP */; 2179 if (c == EOF || getchar() == '\n') { 2180 fputs("Macro not defined - 4K buffer exceeded.\n", ttyout); 2181 code = -1; 2182 return; 2183 } 2184 } 2185 } 2186 2187 /* 2188 * Get size of file on remote machine 2189 */ 2190 void 2191 sizecmd(int argc, char *argv[]) 2192 { 2193 off_t size; 2194 2195 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 2196 fprintf(ttyout, "usage: %s file\n", argv[0]); 2197 code = -1; 2198 return; 2199 } 2200 size = remotesize(argv[1], 1); 2201 if (size != -1) 2202 fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size); 2203 code = size; 2204 } 2205 2206 /* 2207 * Get last modification time of file on remote machine 2208 */ 2209 void 2210 modtime(int argc, char *argv[]) 2211 { 2212 time_t mtime; 2213 2214 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 2215 fprintf(ttyout, "usage: %s file\n", argv[0]); 2216 code = -1; 2217 return; 2218 } 2219 mtime = remotemodtime(argv[1], 1); 2220 if (mtime != -1) 2221 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); 2222 code = mtime; 2223 } 2224 2225 /* 2226 * Show status on remote machine 2227 */ 2228 void 2229 rmtstatus(int argc, char *argv[]) 2230 { 2231 2232 (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]); 2233 } 2234 2235 /* 2236 * Get file if modtime is more recent than current file 2237 */ 2238 void 2239 newer(int argc, char *argv[]) 2240 { 2241 2242 if (getit(argc, argv, -1, "w")) 2243 fprintf(ttyout, "Local file \"%s\" is newer than remote file \"%s\".\n", 2244 argv[2], argv[1]); 2245 } 2246 2247 /* 2248 * Display one file through $PAGER (defaults to "more"). 2249 */ 2250 void 2251 page(int argc, char *argv[]) 2252 { 2253 int orestart_point, ohash, overbose; 2254 char *p, *pager, *oldargv1; 2255 2256 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 2257 fprintf(ttyout, "usage: %s file\n", argv[0]); 2258 code = -1; 2259 return; 2260 } 2261 oldargv1 = argv[1]; 2262 if (!globulize(&argv[1])) { 2263 code = -1; 2264 return; 2265 } 2266 p = getenv("PAGER"); 2267 if (p == NULL || (*p == '\0')) 2268 p = PAGER; 2269 if (asprintf(&pager, "|%s", p) == -1) 2270 errx(1, "Can't allocate memory for $PAGER"); 2271 2272 orestart_point = restart_point; 2273 ohash = hash; 2274 overbose = verbose; 2275 restart_point = hash = verbose = 0; 2276 recvrequest("RETR", pager, argv[1], "r+w", 1, 0); 2277 (void)free(pager); 2278 restart_point = orestart_point; 2279 hash = ohash; 2280 verbose = overbose; 2281 if (oldargv1 != argv[1]) /* free up after globulize() */ 2282 free(argv[1]); 2283 } 2284