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