1 /* $NetBSD: cmds.c,v 1.97 2002/05/07 02:04:09 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1996-2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 12 * NASA Ames Research Center. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by the NetBSD 25 * Foundation, Inc. and its contributors. 26 * 4. Neither the name of The NetBSD Foundation nor the names of its 27 * contributors may be used to endorse or promote products derived 28 * from this software without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 * POSSIBILITY OF SUCH DAMAGE. 41 */ 42 43 /* 44 * Copyright (c) 1985, 1989, 1993, 1994 45 * The Regents of the University of California. All rights reserved. 46 * 47 * Redistribution and use in source and binary forms, with or without 48 * modification, are permitted provided that the following conditions 49 * are met: 50 * 1. Redistributions of source code must retain the above copyright 51 * notice, this list of conditions and the following disclaimer. 52 * 2. Redistributions in binary form must reproduce the above copyright 53 * notice, this list of conditions and the following disclaimer in the 54 * documentation and/or other materials provided with the distribution. 55 * 3. All advertising materials mentioning features or use of this software 56 * must display the following acknowledgement: 57 * This product includes software developed by the University of 58 * California, Berkeley and its contributors. 59 * 4. Neither the name of the University nor the names of its contributors 60 * may be used to endorse or promote products derived from this software 61 * without specific prior written permission. 62 * 63 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 64 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 65 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 66 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 67 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 68 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 69 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 71 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 72 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 73 * SUCH DAMAGE. 74 */ 75 76 /* 77 * Copyright (C) 1997 and 1998 WIDE Project. 78 * All rights reserved. 79 * 80 * Redistribution and use in source and binary forms, with or without 81 * modification, are permitted provided that the following conditions 82 * are met: 83 * 1. Redistributions of source code must retain the above copyright 84 * notice, this list of conditions and the following disclaimer. 85 * 2. Redistributions in binary form must reproduce the above copyright 86 * notice, this list of conditions and the following disclaimer in the 87 * documentation and/or other materials provided with the distribution. 88 * 3. Neither the name of the project nor the names of its contributors 89 * may be used to endorse or promote products derived from this software 90 * without specific prior written permission. 91 * 92 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 93 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 94 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 95 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 96 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 97 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 98 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 99 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 100 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 101 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 102 * SUCH DAMAGE. 103 */ 104 105 #include <sys/cdefs.h> 106 #ifndef lint 107 #if 0 108 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; 109 #else 110 __RCSID("$NetBSD: cmds.c,v 1.97 2002/05/07 02:04:09 lukem Exp $"); 111 #endif 112 #endif /* not lint */ 113 114 /* 115 * FTP User Program -- Command Routines. 116 */ 117 #include <sys/types.h> 118 #include <sys/socket.h> 119 #include <sys/stat.h> 120 #include <sys/wait.h> 121 #include <arpa/ftp.h> 122 123 #include <ctype.h> 124 #include <err.h> 125 #include <glob.h> 126 #include <limits.h> 127 #include <netdb.h> 128 #include <paths.h> 129 #include <stdio.h> 130 #include <stdlib.h> 131 #include <string.h> 132 #include <time.h> 133 #include <unistd.h> 134 #include <util.h> 135 136 #include "ftp_var.h" 137 #include "version.h" 138 139 struct types { 140 char *t_name; 141 char *t_mode; 142 int t_type; 143 char *t_arg; 144 } types[] = { 145 { "ascii", "A", TYPE_A, 0 }, 146 { "binary", "I", TYPE_I, 0 }, 147 { "image", "I", TYPE_I, 0 }, 148 { "ebcdic", "E", TYPE_E, 0 }, 149 { "tenex", "L", TYPE_L, bytename }, 150 { NULL } 151 }; 152 153 sigjmp_buf jabort; 154 char *mname; 155 156 static int confirm(const char *, const char *); 157 158 static int 159 confirm(const char *cmd, const char *file) 160 { 161 char line[BUFSIZ]; 162 163 if (!interactive || confirmrest) 164 return (1); 165 while (1) { 166 fprintf(ttyout, "%s %s [anpqy?]? ", cmd, file); 167 (void)fflush(ttyout); 168 if (fgets(line, sizeof(line), stdin) == NULL) { 169 mflag = 0; 170 fprintf(ttyout, "\nEOF received; %s aborted\n", mname); 171 clearerr(stdin); 172 return (0); 173 } 174 switch (tolower(*line)) { 175 case 'a': 176 confirmrest = 1; 177 fprintf(ttyout, 178 "Prompting off for duration of %s.\n", cmd); 179 break; 180 case 'p': 181 interactive = 0; 182 fputs("Interactive mode: off.\n", ttyout); 183 break; 184 case 'q': 185 mflag = 0; 186 fprintf(ttyout, "%s aborted.\n", mname); 187 /* FALLTHROUGH */ 188 case 'n': 189 return (0); 190 case '?': 191 fprintf(ttyout, 192 " confirmation options:\n" 193 "\ta answer `yes' for the duration of %s\n" 194 "\tn answer `no' for this file\n" 195 "\tp turn off `prompt' mode\n" 196 "\tq stop the current %s\n" 197 "\ty answer `yes' for this file\n" 198 "\t? this help list\n", 199 cmd, cmd); 200 continue; /* back to while(1) */ 201 } 202 return (1); 203 } 204 /* NOTREACHED */ 205 } 206 207 /* 208 * Set transfer type. 209 */ 210 void 211 settype(int argc, char *argv[]) 212 { 213 struct types *p; 214 int comret; 215 216 if (argc == 0 || argc > 2) { 217 char *sep; 218 219 fprintf(ttyout, "usage: %s [", argv[0]); 220 sep = " "; 221 for (p = types; p->t_name; p++) { 222 fprintf(ttyout, "%s%s", sep, p->t_name); 223 sep = " | "; 224 } 225 fputs(" ]\n", ttyout); 226 code = -1; 227 return; 228 } 229 if (argc < 2) { 230 fprintf(ttyout, "Using %s mode to transfer files.\n", typename); 231 code = 0; 232 return; 233 } 234 for (p = types; p->t_name; p++) 235 if (strcmp(argv[1], p->t_name) == 0) 236 break; 237 if (p->t_name == 0) { 238 fprintf(ttyout, "%s: unknown mode.\n", argv[1]); 239 code = -1; 240 return; 241 } 242 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) 243 comret = command("TYPE %s %s", p->t_mode, p->t_arg); 244 else 245 comret = command("TYPE %s", p->t_mode); 246 if (comret == COMPLETE) { 247 (void)strlcpy(typename, p->t_name, sizeof(typename)); 248 curtype = type = p->t_type; 249 } 250 } 251 252 /* 253 * Internal form of settype; changes current type in use with server 254 * without changing our notion of the type for data transfers. 255 * Used to change to and from ascii for listings. 256 */ 257 void 258 changetype(int newtype, int show) 259 { 260 struct types *p; 261 int comret, oldverbose = verbose; 262 263 if (newtype == 0) 264 newtype = TYPE_I; 265 if (newtype == curtype) 266 return; 267 if (debug == 0 && show == 0) 268 verbose = 0; 269 for (p = types; p->t_name; p++) 270 if (newtype == p->t_type) 271 break; 272 if (p->t_name == 0) { 273 warnx("internal error: unknown type %d.", newtype); 274 return; 275 } 276 if (newtype == TYPE_L && bytename[0] != '\0') 277 comret = command("TYPE %s %s", p->t_mode, bytename); 278 else 279 comret = command("TYPE %s", p->t_mode); 280 if (comret == COMPLETE) 281 curtype = newtype; 282 verbose = oldverbose; 283 } 284 285 char *stype[] = { 286 "type", 287 "", 288 0 289 }; 290 291 /* 292 * Set binary transfer type. 293 */ 294 /*VARARGS*/ 295 void 296 setbinary(int argc, char *argv[]) 297 { 298 299 if (argc == 0) { 300 fprintf(ttyout, "usage: %s\n", argv[0]); 301 code = -1; 302 return; 303 } 304 stype[1] = "binary"; 305 settype(2, stype); 306 } 307 308 /* 309 * Set ascii transfer type. 310 */ 311 /*VARARGS*/ 312 void 313 setascii(int argc, char *argv[]) 314 { 315 316 if (argc == 0) { 317 fprintf(ttyout, "usage: %s\n", argv[0]); 318 code = -1; 319 return; 320 } 321 stype[1] = "ascii"; 322 settype(2, stype); 323 } 324 325 /* 326 * Set tenex transfer type. 327 */ 328 /*VARARGS*/ 329 void 330 settenex(int argc, char *argv[]) 331 { 332 333 if (argc == 0) { 334 fprintf(ttyout, "usage: %s\n", argv[0]); 335 code = -1; 336 return; 337 } 338 stype[1] = "tenex"; 339 settype(2, stype); 340 } 341 342 /* 343 * Set file transfer mode. 344 */ 345 /*ARGSUSED*/ 346 void 347 setftmode(int argc, char *argv[]) 348 { 349 350 if (argc != 2) { 351 fprintf(ttyout, "usage: %s mode-name\n", argv[0]); 352 code = -1; 353 return; 354 } 355 fprintf(ttyout, "We only support %s mode, sorry.\n", modename); 356 code = -1; 357 } 358 359 /* 360 * Set file transfer format. 361 */ 362 /*ARGSUSED*/ 363 void 364 setform(int argc, char *argv[]) 365 { 366 367 if (argc != 2) { 368 fprintf(ttyout, "usage: %s format\n", argv[0]); 369 code = -1; 370 return; 371 } 372 fprintf(ttyout, "We only support %s format, sorry.\n", formname); 373 code = -1; 374 } 375 376 /* 377 * Set file transfer structure. 378 */ 379 /*ARGSUSED*/ 380 void 381 setstruct(int argc, char *argv[]) 382 { 383 384 if (argc != 2) { 385 fprintf(ttyout, "usage: %s struct-mode\n", argv[0]); 386 code = -1; 387 return; 388 } 389 fprintf(ttyout, "We only support %s structure, sorry.\n", structname); 390 code = -1; 391 } 392 393 /* 394 * Send a single file. 395 */ 396 void 397 put(int argc, char *argv[]) 398 { 399 char *cmd; 400 int loc = 0; 401 char *locfile, *remfile; 402 403 if (argc == 2) { 404 argc++; 405 argv[2] = argv[1]; 406 loc++; 407 } 408 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file"))) 409 goto usage; 410 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 411 usage: 412 fprintf(ttyout, "usage: %s local-file [remote-file]\n", 413 argv[0]); 414 code = -1; 415 return; 416 } 417 if ((locfile = globulize(argv[1])) == NULL) { 418 code = -1; 419 return; 420 } 421 remfile = argv[2]; 422 if (loc) /* If argv[2] is a copy of the old argv[1], update it */ 423 remfile = locfile; 424 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR"); 425 if (loc && ntflag) 426 remfile = dotrans(remfile); 427 if (loc && mapflag) 428 remfile = domap(remfile); 429 sendrequest(cmd, locfile, remfile, 430 locfile != argv[1] || remfile != argv[2]); 431 free(locfile); 432 } 433 434 /* 435 * Send multiple files. 436 */ 437 void 438 mput(int argc, char *argv[]) 439 { 440 int i; 441 sigfunc oldintr; 442 int ointer; 443 char *tp; 444 445 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) { 446 fprintf(ttyout, "usage: %s local-files\n", argv[0]); 447 code = -1; 448 return; 449 } 450 mname = argv[0]; 451 mflag = 1; 452 oldintr = xsignal(SIGINT, mintr); 453 if (sigsetjmp(jabort, 1)) 454 mabort(); 455 if (proxy) { 456 char *cp; 457 458 while ((cp = remglob(argv, 0, NULL)) != NULL) { 459 if (*cp == '\0' || !connected) { 460 mflag = 0; 461 continue; 462 } 463 if (mflag && confirm(argv[0], cp)) { 464 tp = cp; 465 if (mcase) 466 tp = docase(tp); 467 if (ntflag) 468 tp = dotrans(tp); 469 if (mapflag) 470 tp = domap(tp); 471 sendrequest((sunique) ? "STOU" : "STOR", 472 cp, tp, cp != tp || !interactive); 473 if (!mflag && fromatty) { 474 ointer = interactive; 475 interactive = 1; 476 if (confirm("Continue with", "mput")) { 477 mflag++; 478 } 479 interactive = ointer; 480 } 481 } 482 } 483 goto cleanupmput; 484 } 485 for (i = 1; i < argc && connected; i++) { 486 char **cpp; 487 glob_t gl; 488 int flags; 489 490 if (!doglob) { 491 if (mflag && confirm(argv[0], argv[i])) { 492 tp = (ntflag) ? dotrans(argv[i]) : argv[i]; 493 tp = (mapflag) ? domap(tp) : tp; 494 sendrequest((sunique) ? "STOU" : "STOR", 495 argv[i], tp, tp != argv[i] || !interactive); 496 if (!mflag && fromatty) { 497 ointer = interactive; 498 interactive = 1; 499 if (confirm("Continue with", "mput")) { 500 mflag++; 501 } 502 interactive = ointer; 503 } 504 } 505 continue; 506 } 507 508 memset(&gl, 0, sizeof(gl)); 509 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 510 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { 511 warnx("%s: not found", argv[i]); 512 globfree(&gl); 513 continue; 514 } 515 for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected; 516 cpp++) { 517 if (mflag && confirm(argv[0], *cpp)) { 518 tp = (ntflag) ? dotrans(*cpp) : *cpp; 519 tp = (mapflag) ? domap(tp) : tp; 520 sendrequest((sunique) ? "STOU" : "STOR", 521 *cpp, tp, *cpp != tp || !interactive); 522 if (!mflag && fromatty) { 523 ointer = interactive; 524 interactive = 1; 525 if (confirm("Continue with", "mput")) { 526 mflag++; 527 } 528 interactive = ointer; 529 } 530 } 531 } 532 globfree(&gl); 533 } 534 cleanupmput: 535 (void)xsignal(SIGINT, oldintr); 536 mflag = 0; 537 } 538 539 void 540 reget(int argc, char *argv[]) 541 { 542 543 (void)getit(argc, argv, 1, "r+"); 544 } 545 546 void 547 get(int argc, char *argv[]) 548 { 549 550 (void)getit(argc, argv, 0, restart_point ? "r+" : "w" ); 551 } 552 553 /* 554 * Receive one file. 555 * If restartit is 1, restart the xfer always. 556 * If restartit is -1, restart the xfer only if the remote file is newer. 557 */ 558 int 559 getit(int argc, char *argv[], int restartit, const char *mode) 560 { 561 int loc, rval; 562 char *remfile, *locfile, *olocfile; 563 564 loc = rval = 0; 565 if (argc == 2) { 566 argc++; 567 argv[2] = argv[1]; 568 loc++; 569 } 570 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file"))) 571 goto usage; 572 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { 573 usage: 574 fprintf(ttyout, "usage: %s remote-file [local-file]\n", 575 argv[0]); 576 code = -1; 577 return (0); 578 } 579 remfile = argv[1]; 580 if ((olocfile = globulize(argv[2])) == NULL) { 581 code = -1; 582 return (0); 583 } 584 locfile = olocfile; 585 if (loc && mcase) 586 locfile = docase(locfile); 587 if (loc && ntflag) 588 locfile = dotrans(locfile); 589 if (loc && mapflag) 590 locfile = domap(locfile); 591 if (restartit) { 592 struct stat stbuf; 593 int ret; 594 595 if (! features[FEAT_REST_STREAM]) { 596 fprintf(ttyout, 597 "Restart is not supported by the remote server.\n"); 598 return (0); 599 } 600 ret = stat(locfile, &stbuf); 601 if (restartit == 1) { 602 if (ret < 0) { 603 warn("local: %s", locfile); 604 goto freegetit; 605 } 606 restart_point = stbuf.st_size; 607 } else { 608 if (ret == 0) { 609 time_t mtime; 610 611 mtime = remotemodtime(argv[1], 0); 612 if (mtime == -1) 613 goto freegetit; 614 if (stbuf.st_mtime >= mtime) { 615 rval = 1; 616 goto freegetit; 617 } 618 } 619 } 620 } 621 622 recvrequest("RETR", locfile, remfile, mode, 623 remfile != argv[1] || locfile != argv[2], loc); 624 restart_point = 0; 625 freegetit: 626 (void)free(olocfile); 627 return (rval); 628 } 629 630 /* ARGSUSED */ 631 void 632 mintr(int signo) 633 { 634 635 alarmtimer(0); 636 if (fromatty) 637 write(fileno(ttyout), "\n", 1); 638 siglongjmp(jabort, 1); 639 } 640 641 void 642 mabort(void) 643 { 644 int ointer, oconf; 645 646 if (mflag && fromatty) { 647 ointer = interactive; 648 oconf = confirmrest; 649 interactive = 1; 650 confirmrest = 0; 651 if (confirm("Continue with", mname)) { 652 interactive = ointer; 653 confirmrest = oconf; 654 return; 655 } 656 interactive = ointer; 657 confirmrest = oconf; 658 } 659 mflag = 0; 660 } 661 662 /* 663 * Get multiple files. 664 */ 665 void 666 mget(int argc, char *argv[]) 667 { 668 sigfunc oldintr; 669 int ointer; 670 char *cp, *tp; 671 int restartit; 672 673 if (argc == 0 || 674 (argc == 1 && !another(&argc, &argv, "remote-files"))) { 675 fprintf(ttyout, "usage: %s remote-files\n", argv[0]); 676 code = -1; 677 return; 678 } 679 mname = argv[0]; 680 mflag = 1; 681 restart_point = 0; 682 restartit = 0; 683 if (strcmp(argv[0], "mreget") == 0) { 684 if (! features[FEAT_REST_STREAM]) { 685 fprintf(ttyout, 686 "Restart is not supported by the remote server.\n"); 687 return; 688 } 689 restartit = 1; 690 } 691 oldintr = xsignal(SIGINT, mintr); 692 if (sigsetjmp(jabort, 1)) 693 mabort(); 694 while ((cp = remglob(argv, proxy, NULL)) != NULL) { 695 if (*cp == '\0' || !connected) { 696 mflag = 0; 697 continue; 698 } 699 if (! mflag || !confirm(argv[0], cp)) 700 continue; 701 tp = cp; 702 if (mcase) 703 tp = docase(tp); 704 if (ntflag) 705 tp = dotrans(tp); 706 if (mapflag) 707 tp = domap(tp); 708 if (restartit) { 709 struct stat stbuf; 710 711 if (stat(tp, &stbuf) == 0) 712 restart_point = stbuf.st_size; 713 else 714 warn("stat %s", tp); 715 } 716 recvrequest("RETR", tp, cp, restart_point ? "r+" : "w", 717 tp != cp || !interactive, 1); 718 restart_point = 0; 719 if (!mflag && fromatty) { 720 ointer = interactive; 721 interactive = 1; 722 if (confirm("Continue with", "mget")) 723 mflag++; 724 interactive = ointer; 725 } 726 } 727 (void)xsignal(SIGINT, oldintr); 728 mflag = 0; 729 } 730 731 /* 732 * Read list of filenames from a local file and get those 733 */ 734 void 735 fget(int argc, char *argv[]) 736 { 737 char *buf, *mode; 738 FILE *fp; 739 740 if (argc != 2) { 741 fprintf(ttyout, "usage: %s localfile\n", argv[0]); 742 code = -1; 743 return; 744 } 745 746 fp = fopen(argv[1], "r"); 747 if (fp == NULL) { 748 fprintf(ttyout, "Cannot open source file %s\n", argv[1]); 749 code = -1; 750 return; 751 } 752 753 argv[0] = "get"; 754 mode = restart_point ? "r+" : "w"; 755 756 for (; 757 (buf = fparseln(fp, NULL, NULL, "\0\0\0", 0)) != NULL; 758 free(buf)) { 759 if (buf[0] == '\0') 760 continue; 761 argv[1] = buf; 762 (void)getit(argc, argv, 0, mode); 763 } 764 fclose(fp); 765 } 766 767 char * 768 onoff(int bool) 769 { 770 771 return (bool ? "on" : "off"); 772 } 773 774 /* 775 * Show status. 776 */ 777 /*ARGSUSED*/ 778 void 779 status(int argc, char *argv[]) 780 { 781 int i; 782 783 if (argc == 0) { 784 fprintf(ttyout, "usage: %s\n", argv[0]); 785 code = -1; 786 return; 787 } 788 if (connected) 789 fprintf(ttyout, "Connected %sto %s.\n", 790 connected == -1 ? "and logged in" : "", hostname); 791 else 792 fputs("Not connected.\n", ttyout); 793 if (!proxy) { 794 pswitch(1); 795 if (connected) { 796 fprintf(ttyout, "Connected for proxy commands to %s.\n", 797 hostname); 798 } 799 else { 800 fputs("No proxy connection.\n", ttyout); 801 } 802 pswitch(0); 803 } 804 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), 805 *gateserver ? gateserver : "(none)", gateport); 806 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 807 onoff(passivemode), onoff(activefallback)); 808 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", 809 modename, typename, formname, structname); 810 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", 811 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob)); 812 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", 813 onoff(sunique), onoff(runique)); 814 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); 815 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), 816 onoff(crflag)); 817 if (ntflag) { 818 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); 819 } 820 else { 821 fputs("Ntrans: off.\n", ttyout); 822 } 823 if (mapflag) { 824 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); 825 } 826 else { 827 fputs("Nmap: off.\n", ttyout); 828 } 829 fprintf(ttyout, 830 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", 831 onoff(hash), mark, onoff(progress)); 832 fprintf(ttyout, 833 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 834 onoff(rate_get), rate_get, rate_get_incr); 835 fprintf(ttyout, 836 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 837 onoff(rate_put), rate_put, rate_put_incr); 838 fprintf(ttyout, 839 "Socket buffer sizes: send %d, receive %d.\n", 840 sndbuf_size, rcvbuf_size); 841 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport)); 842 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), 843 epsv4bad ? " (disabled for this connection)" : ""); 844 fprintf(ttyout, "Command line editing: %s.\n", 845 #ifdef NO_EDITCOMPLETE 846 "support not compiled in" 847 #else /* !def NO_EDITCOMPLETE */ 848 onoff(editing) 849 #endif /* !def NO_EDITCOMPLETE */ 850 ); 851 fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION); 852 if (macnum > 0) { 853 fputs("Macros:\n", ttyout); 854 for (i=0; i<macnum; i++) { 855 fprintf(ttyout, "\t%s\n", macros[i].mac_name); 856 } 857 } 858 code = 0; 859 } 860 861 /* 862 * Toggle a variable 863 */ 864 int 865 togglevar(int argc, char *argv[], int *var, const char *mesg) 866 { 867 if (argc == 1) { 868 *var = !*var; 869 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { 870 *var = 1; 871 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { 872 *var = 0; 873 } else { 874 fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]); 875 return (-1); 876 } 877 if (mesg) 878 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); 879 return (*var); 880 } 881 882 /* 883 * Set beep on cmd completed mode. 884 */ 885 /*VARARGS*/ 886 void 887 setbell(int argc, char *argv[]) 888 { 889 890 code = togglevar(argc, argv, &bell, "Bell mode"); 891 } 892 893 /* 894 * Set command line editing 895 */ 896 /*VARARGS*/ 897 void 898 setedit(int argc, char *argv[]) 899 { 900 901 #ifdef NO_EDITCOMPLETE 902 if (argc == 0) { 903 fprintf(ttyout, "usage: %s\n", argv[0]); 904 code = -1; 905 return; 906 } 907 if (verbose) 908 fputs("Editing support not compiled in; ignoring command.\n", 909 ttyout); 910 #else /* !def NO_EDITCOMPLETE */ 911 code = togglevar(argc, argv, &editing, "Editing mode"); 912 controlediting(); 913 #endif /* !def NO_EDITCOMPLETE */ 914 } 915 916 /* 917 * Turn on packet tracing. 918 */ 919 /*VARARGS*/ 920 void 921 settrace(int argc, char *argv[]) 922 { 923 924 code = togglevar(argc, argv, &trace, "Packet tracing"); 925 } 926 927 /* 928 * Toggle hash mark printing during transfers, or set hash mark bytecount. 929 */ 930 /*VARARGS*/ 931 void 932 sethash(int argc, char *argv[]) 933 { 934 if (argc == 1) 935 hash = !hash; 936 else if (argc != 2) { 937 fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n", 938 argv[0]); 939 code = -1; 940 return; 941 } else if (strcasecmp(argv[1], "on") == 0) 942 hash = 1; 943 else if (strcasecmp(argv[1], "off") == 0) 944 hash = 0; 945 else { 946 int nmark; 947 948 nmark = strsuftoi(argv[1]); 949 if (nmark < 1) { 950 fprintf(ttyout, "mark: bad bytecount value `%s'.\n", 951 argv[1]); 952 code = -1; 953 return; 954 } 955 mark = nmark; 956 hash = 1; 957 } 958 fprintf(ttyout, "Hash mark printing %s", onoff(hash)); 959 if (hash) 960 fprintf(ttyout, " (%d bytes/hash mark)", mark); 961 fputs(".\n", ttyout); 962 if (hash) 963 progress = 0; 964 code = hash; 965 } 966 967 /* 968 * Turn on printing of server echo's. 969 */ 970 /*VARARGS*/ 971 void 972 setverbose(int argc, char *argv[]) 973 { 974 975 code = togglevar(argc, argv, &verbose, "Verbose mode"); 976 } 977 978 /* 979 * Toggle PORT/LPRT cmd use before each data connection. 980 */ 981 /*VARARGS*/ 982 void 983 setport(int argc, char *argv[]) 984 { 985 986 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); 987 } 988 989 /* 990 * Toggle transfer progress bar. 991 */ 992 /*VARARGS*/ 993 void 994 setprogress(int argc, char *argv[]) 995 { 996 997 code = togglevar(argc, argv, &progress, "Progress bar"); 998 if (progress) 999 hash = 0; 1000 } 1001 1002 /* 1003 * Turn on interactive prompting during mget, mput, and mdelete. 1004 */ 1005 /*VARARGS*/ 1006 void 1007 setprompt(int argc, char *argv[]) 1008 { 1009 1010 code = togglevar(argc, argv, &interactive, "Interactive mode"); 1011 } 1012 1013 /* 1014 * Toggle gate-ftp mode, or set gate-ftp server 1015 */ 1016 /*VARARGS*/ 1017 void 1018 setgate(int argc, char *argv[]) 1019 { 1020 static char gsbuf[MAXHOSTNAMELEN]; 1021 1022 if (argc == 0 || argc > 3) { 1023 fprintf(ttyout, 1024 "usage: %s [ on | off | gateserver [port] ]\n", argv[0]); 1025 code = -1; 1026 return; 1027 } else if (argc < 2) { 1028 gatemode = !gatemode; 1029 } else { 1030 if (argc == 2 && strcasecmp(argv[1], "on") == 0) 1031 gatemode = 1; 1032 else if (argc == 2 && strcasecmp(argv[1], "off") == 0) 1033 gatemode = 0; 1034 else { 1035 if (argc == 3) 1036 gateport = strdup(argv[2]); 1037 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf)); 1038 gateserver = gsbuf; 1039 gatemode = 1; 1040 } 1041 } 1042 if (gatemode && (gateserver == NULL || *gateserver == '\0')) { 1043 fprintf(ttyout, 1044 "Disabling gate-ftp mode - no gate-ftp server defined.\n"); 1045 gatemode = 0; 1046 } else { 1047 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", 1048 onoff(gatemode), *gateserver ? gateserver : "(none)", 1049 gateport); 1050 } 1051 code = gatemode; 1052 } 1053 1054 /* 1055 * Toggle metacharacter interpretation on local file names. 1056 */ 1057 /*VARARGS*/ 1058 void 1059 setglob(int argc, char *argv[]) 1060 { 1061 1062 code = togglevar(argc, argv, &doglob, "Globbing"); 1063 } 1064 1065 /* 1066 * Toggle preserving modification times on retrieved files. 1067 */ 1068 /*VARARGS*/ 1069 void 1070 setpreserve(int argc, char *argv[]) 1071 { 1072 1073 code = togglevar(argc, argv, &preserve, "Preserve modification times"); 1074 } 1075 1076 /* 1077 * Set debugging mode on/off and/or set level of debugging. 1078 */ 1079 /*VARARGS*/ 1080 void 1081 setdebug(int argc, char *argv[]) 1082 { 1083 if (argc == 0 || argc > 2) { 1084 fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n", 1085 argv[0]); 1086 code = -1; 1087 return; 1088 } else if (argc == 2) { 1089 if (strcasecmp(argv[1], "on") == 0) 1090 debug = 1; 1091 else if (strcasecmp(argv[1], "off") == 0) 1092 debug = 0; 1093 else { 1094 int val; 1095 1096 val = strsuftoi(argv[1]); 1097 if (val < 0) { 1098 fprintf(ttyout, "%s: bad debugging value.\n", 1099 argv[1]); 1100 code = -1; 1101 return; 1102 } 1103 debug = val; 1104 } 1105 } else 1106 debug = !debug; 1107 if (debug) 1108 options |= SO_DEBUG; 1109 else 1110 options &= ~SO_DEBUG; 1111 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug); 1112 code = debug > 0; 1113 } 1114 1115 /* 1116 * Set current working directory on remote machine. 1117 */ 1118 void 1119 cd(int argc, char *argv[]) 1120 { 1121 int r; 1122 1123 if (argc == 0 || argc > 2 || 1124 (argc == 1 && !another(&argc, &argv, "remote-directory"))) { 1125 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); 1126 code = -1; 1127 return; 1128 } 1129 r = command("CWD %s", argv[1]); 1130 if (r == ERROR && code == 500) { 1131 if (verbose) 1132 fputs("CWD command not recognized, trying XCWD.\n", 1133 ttyout); 1134 r = command("XCWD %s", argv[1]); 1135 } 1136 if (r == COMPLETE) { 1137 dirchange = 1; 1138 updateremotepwd(); 1139 } 1140 } 1141 1142 /* 1143 * Set current working directory on local machine. 1144 */ 1145 void 1146 lcd(int argc, char *argv[]) 1147 { 1148 char buf[MAXPATHLEN]; 1149 char *locdir; 1150 1151 code = -1; 1152 if (argc == 1) { 1153 argc++; 1154 argv[1] = localhome; 1155 } 1156 if (argc != 2) { 1157 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]); 1158 return; 1159 } 1160 if ((locdir = globulize(argv[1])) == NULL) 1161 return; 1162 if (chdir(locdir) < 0) 1163 warn("local: %s", locdir); 1164 else { 1165 if (getcwd(buf, sizeof(buf)) != NULL) { 1166 fprintf(ttyout, "Local directory now %s\n", buf); 1167 code = 0; 1168 } else 1169 warn("getcwd: %s", locdir); 1170 } 1171 (void)free(locdir); 1172 } 1173 1174 /* 1175 * Delete a single file. 1176 */ 1177 void 1178 delete(int argc, char *argv[]) 1179 { 1180 1181 1182 if (argc == 0 || argc > 2 || 1183 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 1184 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 1185 code = -1; 1186 return; 1187 } 1188 (void)command("DELE %s", argv[1]); 1189 } 1190 1191 /* 1192 * Delete multiple files. 1193 */ 1194 void 1195 mdelete(int argc, char *argv[]) 1196 { 1197 sigfunc oldintr; 1198 int ointer; 1199 char *cp; 1200 1201 if (argc == 0 || 1202 (argc == 1 && !another(&argc, &argv, "remote-files"))) { 1203 fprintf(ttyout, "usage: %s [remote-files]\n", argv[0]); 1204 code = -1; 1205 return; 1206 } 1207 mname = argv[0]; 1208 mflag = 1; 1209 oldintr = xsignal(SIGINT, mintr); 1210 if (sigsetjmp(jabort, 1)) 1211 mabort(); 1212 while ((cp = remglob(argv, 0, NULL)) != NULL) { 1213 if (*cp == '\0') { 1214 mflag = 0; 1215 continue; 1216 } 1217 if (mflag && confirm(argv[0], cp)) { 1218 (void)command("DELE %s", cp); 1219 if (!mflag && fromatty) { 1220 ointer = interactive; 1221 interactive = 1; 1222 if (confirm("Continue with", "mdelete")) { 1223 mflag++; 1224 } 1225 interactive = ointer; 1226 } 1227 } 1228 } 1229 (void)xsignal(SIGINT, oldintr); 1230 mflag = 0; 1231 } 1232 1233 /* 1234 * Rename a remote file. 1235 */ 1236 void 1237 renamefile(int argc, char *argv[]) 1238 { 1239 1240 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name"))) 1241 goto usage; 1242 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { 1243 usage: 1244 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]); 1245 code = -1; 1246 return; 1247 } 1248 if (command("RNFR %s", argv[1]) == CONTINUE) 1249 (void)command("RNTO %s", argv[2]); 1250 } 1251 1252 /* 1253 * Get a directory listing of remote files. 1254 * Supports being invoked as: 1255 * cmd runs 1256 * --- ---- 1257 * dir, ls LIST 1258 * mlsd MLSD 1259 * nlist NLST 1260 * pdir, pls LIST |$PAGER 1261 * mmlsd MLSD |$PAGER 1262 */ 1263 void 1264 ls(int argc, char *argv[]) 1265 { 1266 const char *cmd; 1267 char *remdir, *locfile; 1268 int freelocfile, pagecmd, mlsdcmd; 1269 1270 remdir = NULL; 1271 locfile = "-"; 1272 freelocfile = pagecmd = mlsdcmd = 0; 1273 /* 1274 * the only commands that start with `p' are 1275 * the `pager' versions. 1276 */ 1277 if (argv[0][0] == 'p') 1278 pagecmd = 1; 1279 if (strcmp(argv[0] + pagecmd , "mlsd") == 0) { 1280 if (! features[FEAT_MLST]) { 1281 fprintf(ttyout, 1282 "MLSD is not supported by the remote server.\n"); 1283 return; 1284 } 1285 mlsdcmd = 1; 1286 } 1287 if (argc == 0) 1288 goto usage; 1289 1290 if (mlsdcmd) 1291 cmd = "MLSD"; 1292 else if (strcmp(argv[0] + pagecmd, "nlist") == 0) 1293 cmd = "NLST"; 1294 else 1295 cmd = "LIST"; 1296 1297 if (argc > 1) 1298 remdir = argv[1]; 1299 if (argc > 2) 1300 locfile = argv[2]; 1301 if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) { 1302 usage: 1303 if (pagecmd || mlsdcmd) 1304 fprintf(ttyout, 1305 "usage: %s [remote-path]\n", argv[0]); 1306 else 1307 fprintf(ttyout, 1308 "usage: %s [remote-path [local-file]]\n", 1309 argv[0]); 1310 code = -1; 1311 goto freels; 1312 } 1313 1314 if (pagecmd) { 1315 char *p; 1316 int len; 1317 1318 p = getoptionvalue("pager"); 1319 if (EMPTYSTRING(p)) 1320 p = DEFAULTPAGER; 1321 len = strlen(p) + 2; 1322 locfile = xmalloc(len); 1323 locfile[0] = '|'; 1324 (void)strlcpy(locfile + 1, p, len - 1); 1325 freelocfile = 1; 1326 } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') { 1327 if ((locfile = globulize(locfile)) == NULL || 1328 !confirm("output to local-file:", locfile)) { 1329 code = -1; 1330 goto freels; 1331 } 1332 freelocfile = 1; 1333 } 1334 recvrequest(cmd, locfile, remdir, "w", 0, 0); 1335 freels: 1336 if (freelocfile && locfile) 1337 (void)free(locfile); 1338 } 1339 1340 /* 1341 * Get a directory listing of multiple remote files. 1342 */ 1343 void 1344 mls(int argc, char *argv[]) 1345 { 1346 sigfunc oldintr; 1347 int ointer, i; 1348 int dolist; 1349 char *mode, *dest, *odest; 1350 1351 if (argc == 0) 1352 goto usage; 1353 if (argc < 2 && !another(&argc, &argv, "remote-files")) 1354 goto usage; 1355 if (argc < 3 && !another(&argc, &argv, "local-file")) { 1356 usage: 1357 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]); 1358 code = -1; 1359 return; 1360 } 1361 odest = dest = argv[argc - 1]; 1362 argv[argc - 1] = NULL; 1363 if (strcmp(dest, "-") && *dest != '|') 1364 if (((dest = globulize(dest)) == NULL) || 1365 !confirm("output to local-file:", dest)) { 1366 code = -1; 1367 return; 1368 } 1369 dolist = strcmp(argv[0], "mls"); 1370 mname = argv[0]; 1371 mflag = 1; 1372 oldintr = xsignal(SIGINT, mintr); 1373 if (sigsetjmp(jabort, 1)) 1374 mabort(); 1375 for (i = 1; mflag && i < argc-1 && connected; i++) { 1376 mode = (i == 1) ? "w" : "a"; 1377 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode, 1378 0, 0); 1379 if (!mflag && fromatty) { 1380 ointer = interactive; 1381 interactive = 1; 1382 if (confirm("Continue with", argv[0])) { 1383 mflag ++; 1384 } 1385 interactive = ointer; 1386 } 1387 } 1388 (void)xsignal(SIGINT, oldintr); 1389 mflag = 0; 1390 if (dest != odest) /* free up after globulize() */ 1391 free(dest); 1392 } 1393 1394 /* 1395 * Do a shell escape 1396 */ 1397 /*ARGSUSED*/ 1398 void 1399 shell(int argc, char *argv[]) 1400 { 1401 pid_t pid; 1402 sigfunc old1; 1403 char shellnam[MAXPATHLEN], *shell, *namep; 1404 int wait_status; 1405 1406 if (argc == 0) { 1407 fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]); 1408 code = -1; 1409 return; 1410 } 1411 old1 = xsignal(SIGINT, SIG_IGN); 1412 if ((pid = fork()) == 0) { 1413 for (pid = 3; pid < 20; pid++) 1414 (void)close(pid); 1415 (void)xsignal(SIGINT, SIG_DFL); 1416 shell = getenv("SHELL"); 1417 if (shell == NULL) 1418 shell = _PATH_BSHELL; 1419 namep = strrchr(shell, '/'); 1420 if (namep == NULL) 1421 namep = shell; 1422 else 1423 namep++; 1424 (void)strlcpy(shellnam, namep, sizeof(shellnam)); 1425 if (debug) { 1426 fputs(shell, ttyout); 1427 putc('\n', ttyout); 1428 } 1429 if (argc > 1) { 1430 execl(shell, shellnam, "-c", altarg, (char *)0); 1431 } 1432 else { 1433 execl(shell, shellnam, (char *)0); 1434 } 1435 warn("%s", shell); 1436 code = -1; 1437 exit(1); 1438 } 1439 if (pid > 0) 1440 while (wait(&wait_status) != pid) 1441 ; 1442 (void)xsignal(SIGINT, old1); 1443 if (pid == -1) { 1444 warn("Try again later"); 1445 code = -1; 1446 } else 1447 code = 0; 1448 } 1449 1450 /* 1451 * Send new user information (re-login) 1452 */ 1453 void 1454 user(int argc, char *argv[]) 1455 { 1456 char acct[80]; 1457 int n, aflag = 0; 1458 1459 if (argc == 0) 1460 goto usage; 1461 if (argc < 2) 1462 (void)another(&argc, &argv, "username"); 1463 if (argc < 2 || argc > 4) { 1464 usage: 1465 fprintf(ttyout, "usage: %s username [password [account]]\n", 1466 argv[0]); 1467 code = -1; 1468 return; 1469 } 1470 n = command("USER %s", argv[1]); 1471 if (n == CONTINUE) { 1472 if (argc < 3) { 1473 argv[2] = getpass("Password: "); 1474 argc++; 1475 } 1476 n = command("PASS %s", argv[2]); 1477 } 1478 if (n == CONTINUE) { 1479 if (argc < 4) { 1480 (void)fputs("Account: ", ttyout); 1481 (void)fflush(ttyout); 1482 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) { 1483 fprintf(ttyout, 1484 "\nEOF received; login aborted.\n"); 1485 clearerr(stdin); 1486 code = -1; 1487 return; 1488 } 1489 acct[strlen(acct) - 1] = '\0'; 1490 argv[3] = acct; argc++; 1491 } 1492 n = command("ACCT %s", argv[3]); 1493 aflag++; 1494 } 1495 if (n != COMPLETE) { 1496 fputs("Login failed.\n", ttyout); 1497 return; 1498 } 1499 if (!aflag && argc == 4) { 1500 (void)command("ACCT %s", argv[3]); 1501 } 1502 connected = -1; 1503 getremoteinfo(); 1504 } 1505 1506 /* 1507 * Print working directory on remote machine. 1508 */ 1509 /*VARARGS*/ 1510 void 1511 pwd(int argc, char *argv[]) 1512 { 1513 int oldverbose = verbose; 1514 1515 if (argc == 0) { 1516 fprintf(ttyout, "usage: %s\n", argv[0]); 1517 code = -1; 1518 return; 1519 } 1520 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 1521 if (command("PWD") == ERROR && code == 500) { 1522 fputs("PWD command not recognized, trying XPWD.\n", ttyout); 1523 (void)command("XPWD"); 1524 } 1525 verbose = oldverbose; 1526 } 1527 1528 /* 1529 * Print working directory on local machine. 1530 */ 1531 void 1532 lpwd(int argc, char *argv[]) 1533 { 1534 char buf[MAXPATHLEN]; 1535 1536 if (argc == 0) { 1537 fprintf(ttyout, "usage: %s\n", argv[0]); 1538 code = -1; 1539 return; 1540 } 1541 if (getcwd(buf, sizeof(buf)) != NULL) { 1542 fprintf(ttyout, "Local directory %s\n", buf); 1543 code = 0; 1544 } else { 1545 warn("getcwd"); 1546 code = -1; 1547 } 1548 } 1549 1550 /* 1551 * Make a directory. 1552 */ 1553 void 1554 makedir(int argc, char *argv[]) 1555 { 1556 1557 if (argc == 0 || argc > 2 || 1558 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1559 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1560 code = -1; 1561 return; 1562 } 1563 if (command("MKD %s", argv[1]) == ERROR && code == 500) { 1564 if (verbose) 1565 fputs("MKD command not recognized, trying XMKD.\n", 1566 ttyout); 1567 (void)command("XMKD %s", argv[1]); 1568 } 1569 } 1570 1571 /* 1572 * Remove a directory. 1573 */ 1574 void 1575 removedir(int argc, char *argv[]) 1576 { 1577 1578 if (argc == 0 || argc > 2 || 1579 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1580 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1581 code = -1; 1582 return; 1583 } 1584 if (command("RMD %s", argv[1]) == ERROR && code == 500) { 1585 if (verbose) 1586 fputs("RMD command not recognized, trying XRMD.\n", 1587 ttyout); 1588 (void)command("XRMD %s", argv[1]); 1589 } 1590 } 1591 1592 /* 1593 * Send a line, verbatim, to the remote machine. 1594 */ 1595 void 1596 quote(int argc, char *argv[]) 1597 { 1598 1599 if (argc == 0 || 1600 (argc == 1 && !another(&argc, &argv, "command line to send"))) { 1601 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]); 1602 code = -1; 1603 return; 1604 } 1605 quote1("", argc, argv); 1606 } 1607 1608 /* 1609 * Send a SITE command to the remote machine. The line 1610 * is sent verbatim to the remote machine, except that the 1611 * word "SITE" is added at the front. 1612 */ 1613 void 1614 site(int argc, char *argv[]) 1615 { 1616 1617 if (argc == 0 || 1618 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){ 1619 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]); 1620 code = -1; 1621 return; 1622 } 1623 quote1("SITE ", argc, argv); 1624 } 1625 1626 /* 1627 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1628 * Send the result as a one-line command and get response. 1629 */ 1630 void 1631 quote1(const char *initial, int argc, char *argv[]) 1632 { 1633 int i; 1634 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1635 1636 (void)strlcpy(buf, initial, sizeof(buf)); 1637 for (i = 1; i < argc; i++) { 1638 (void)strlcat(buf, argv[i], sizeof(buf)); 1639 if (i < (argc - 1)) 1640 (void)strlcat(buf, " ", sizeof(buf)); 1641 } 1642 if (command("%s", buf) == PRELIM) { 1643 while (getreply(0) == PRELIM) 1644 continue; 1645 } 1646 } 1647 1648 void 1649 do_chmod(int argc, char *argv[]) 1650 { 1651 1652 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode"))) 1653 goto usage; 1654 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 1655 usage: 1656 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]); 1657 code = -1; 1658 return; 1659 } 1660 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1661 } 1662 1663 #define COMMAND_1ARG(argc, argv, cmd) \ 1664 if (argc == 1) \ 1665 command(cmd); \ 1666 else \ 1667 command(cmd " %s", argv[1]) 1668 1669 void 1670 do_umask(int argc, char *argv[]) 1671 { 1672 int oldverbose = verbose; 1673 1674 if (argc == 0) { 1675 fprintf(ttyout, "usage: %s [umask]\n", argv[0]); 1676 code = -1; 1677 return; 1678 } 1679 verbose = 1; 1680 COMMAND_1ARG(argc, argv, "SITE UMASK"); 1681 verbose = oldverbose; 1682 } 1683 1684 void 1685 idlecmd(int argc, char *argv[]) 1686 { 1687 int oldverbose = verbose; 1688 1689 if (argc < 1 || argc > 2) { 1690 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]); 1691 code = -1; 1692 return; 1693 } 1694 verbose = 1; 1695 COMMAND_1ARG(argc, argv, "SITE IDLE"); 1696 verbose = oldverbose; 1697 } 1698 1699 /* 1700 * Ask the other side for help. 1701 */ 1702 void 1703 rmthelp(int argc, char *argv[]) 1704 { 1705 int oldverbose = verbose; 1706 1707 if (argc == 0) { 1708 fprintf(ttyout, "usage: %s\n", argv[0]); 1709 code = -1; 1710 return; 1711 } 1712 verbose = 1; 1713 COMMAND_1ARG(argc, argv, "HELP"); 1714 verbose = oldverbose; 1715 } 1716 1717 /* 1718 * Terminate session and exit. 1719 * May be called with 0, NULL. 1720 */ 1721 /*VARARGS*/ 1722 void 1723 quit(int argc, char *argv[]) 1724 { 1725 1726 /* this may be called with argc == 0, argv == NULL */ 1727 if (argc == 0 && argv != NULL) { 1728 fprintf(ttyout, "usage: %s\n", argv[0]); 1729 code = -1; 1730 return; 1731 } 1732 if (connected) 1733 disconnect(0, NULL); 1734 pswitch(1); 1735 if (connected) 1736 disconnect(0, NULL); 1737 exit(0); 1738 } 1739 1740 /* 1741 * Terminate session, but don't exit. 1742 * May be called with 0, NULL. 1743 */ 1744 void 1745 disconnect(int argc, char *argv[]) 1746 { 1747 1748 /* this may be called with argc == 0, argv == NULL */ 1749 if (argc == 0 && argv != NULL) { 1750 fprintf(ttyout, "usage: %s\n", argv[0]); 1751 code = -1; 1752 return; 1753 } 1754 if (!connected) 1755 return; 1756 (void)command("QUIT"); 1757 cleanuppeer(); 1758 } 1759 1760 void 1761 account(int argc, char *argv[]) 1762 { 1763 char *ap; 1764 1765 if (argc == 0 || argc > 2) { 1766 fprintf(ttyout, "usage: %s [password]\n", argv[0]); 1767 code = -1; 1768 return; 1769 } 1770 else if (argc == 2) 1771 ap = argv[1]; 1772 else 1773 ap = getpass("Account:"); 1774 (void)command("ACCT %s", ap); 1775 } 1776 1777 sigjmp_buf abortprox; 1778 1779 void 1780 proxabort(int notused) 1781 { 1782 1783 alarmtimer(0); 1784 if (!proxy) { 1785 pswitch(1); 1786 } 1787 if (connected) { 1788 proxflag = 1; 1789 } 1790 else { 1791 proxflag = 0; 1792 } 1793 pswitch(0); 1794 siglongjmp(abortprox, 1); 1795 } 1796 1797 void 1798 doproxy(int argc, char *argv[]) 1799 { 1800 struct cmd *c; 1801 int cmdpos; 1802 sigfunc oldintr; 1803 1804 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { 1805 fprintf(ttyout, "usage: %s command\n", argv[0]); 1806 code = -1; 1807 return; 1808 } 1809 c = getcmd(argv[1]); 1810 if (c == (struct cmd *) -1) { 1811 fputs("?Ambiguous command.\n", ttyout); 1812 code = -1; 1813 return; 1814 } 1815 if (c == 0) { 1816 fputs("?Invalid command.\n", ttyout); 1817 code = -1; 1818 return; 1819 } 1820 if (!c->c_proxy) { 1821 fputs("?Invalid proxy command.\n", ttyout); 1822 code = -1; 1823 return; 1824 } 1825 if (sigsetjmp(abortprox, 1)) { 1826 code = -1; 1827 return; 1828 } 1829 oldintr = xsignal(SIGINT, proxabort); 1830 pswitch(1); 1831 if (c->c_conn && !connected) { 1832 fputs("Not connected.\n", ttyout); 1833 pswitch(0); 1834 (void)xsignal(SIGINT, oldintr); 1835 code = -1; 1836 return; 1837 } 1838 cmdpos = strcspn(line, " \t"); 1839 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1840 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1841 argv[1] = c->c_name; 1842 (*c->c_handler)(argc-1, argv+1); 1843 if (connected) { 1844 proxflag = 1; 1845 } 1846 else { 1847 proxflag = 0; 1848 } 1849 pswitch(0); 1850 (void)xsignal(SIGINT, oldintr); 1851 } 1852 1853 void 1854 setcase(int argc, char *argv[]) 1855 { 1856 1857 code = togglevar(argc, argv, &mcase, "Case mapping"); 1858 } 1859 1860 /* 1861 * convert the given name to lower case if it's all upper case, into 1862 * a static buffer which is returned to the caller 1863 */ 1864 char * 1865 docase(char *name) 1866 { 1867 static char new[MAXPATHLEN]; 1868 int i, dochange; 1869 1870 dochange = 1; 1871 for (i = 0; name[i] != '\0' && i < sizeof(new) - 1; i++) { 1872 new[i] = name[i]; 1873 if (islower((unsigned char)new[i])) 1874 dochange = 0; 1875 } 1876 new[i] = '\0'; 1877 1878 if (dochange) { 1879 for (i = 0; new[i] != '\0'; i++) 1880 if (isupper((unsigned char)new[i])) 1881 new[i] = tolower(new[i]); 1882 } 1883 return (new); 1884 } 1885 1886 void 1887 setcr(int argc, char *argv[]) 1888 { 1889 1890 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1891 } 1892 1893 void 1894 setntrans(int argc, char *argv[]) 1895 { 1896 1897 if (argc == 0 || argc > 3) { 1898 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]); 1899 code = -1; 1900 return; 1901 } 1902 if (argc == 1) { 1903 ntflag = 0; 1904 fputs("Ntrans off.\n", ttyout); 1905 code = ntflag; 1906 return; 1907 } 1908 ntflag++; 1909 code = ntflag; 1910 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1911 if (argc == 2) { 1912 ntout[0] = '\0'; 1913 return; 1914 } 1915 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1916 } 1917 1918 char * 1919 dotrans(char *name) 1920 { 1921 static char new[MAXPATHLEN]; 1922 char *cp1, *cp2 = new; 1923 int i, ostop, found; 1924 1925 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 1926 continue; 1927 for (cp1 = name; *cp1; cp1++) { 1928 found = 0; 1929 for (i = 0; *(ntin + i) && i < 16; i++) { 1930 if (*cp1 == *(ntin + i)) { 1931 found++; 1932 if (i < ostop) { 1933 *cp2++ = *(ntout + i); 1934 } 1935 break; 1936 } 1937 } 1938 if (!found) { 1939 *cp2++ = *cp1; 1940 } 1941 } 1942 *cp2 = '\0'; 1943 return (new); 1944 } 1945 1946 void 1947 setnmap(int argc, char *argv[]) 1948 { 1949 char *cp; 1950 1951 if (argc == 1) { 1952 mapflag = 0; 1953 fputs("Nmap off.\n", ttyout); 1954 code = mapflag; 1955 return; 1956 } 1957 if (argc == 0 || 1958 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { 1959 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]); 1960 code = -1; 1961 return; 1962 } 1963 mapflag = 1; 1964 code = 1; 1965 cp = strchr(altarg, ' '); 1966 if (proxy) { 1967 while(*++cp == ' ') 1968 continue; 1969 altarg = cp; 1970 cp = strchr(altarg, ' '); 1971 } 1972 *cp = '\0'; 1973 (void)strlcpy(mapin, altarg, MAXPATHLEN); 1974 while (*++cp == ' ') 1975 continue; 1976 (void)strlcpy(mapout, cp, MAXPATHLEN); 1977 } 1978 1979 char * 1980 domap(char *name) 1981 { 1982 static char new[MAXPATHLEN]; 1983 char *cp1 = name, *cp2 = mapin; 1984 char *tp[9], *te[9]; 1985 int i, toks[9], toknum = 0, match = 1; 1986 1987 for (i=0; i < 9; ++i) { 1988 toks[i] = 0; 1989 } 1990 while (match && *cp1 && *cp2) { 1991 switch (*cp2) { 1992 case '\\': 1993 if (*++cp2 != *cp1) { 1994 match = 0; 1995 } 1996 break; 1997 case '$': 1998 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 1999 if (*cp1 != *(++cp2+1)) { 2000 toks[toknum = *cp2 - '1']++; 2001 tp[toknum] = cp1; 2002 while (*++cp1 && *(cp2+1) 2003 != *cp1); 2004 te[toknum] = cp1; 2005 } 2006 cp2++; 2007 break; 2008 } 2009 /* FALLTHROUGH */ 2010 default: 2011 if (*cp2 != *cp1) { 2012 match = 0; 2013 } 2014 break; 2015 } 2016 if (match && *cp1) { 2017 cp1++; 2018 } 2019 if (match && *cp2) { 2020 cp2++; 2021 } 2022 } 2023 if (!match && *cp1) /* last token mismatch */ 2024 { 2025 toks[toknum] = 0; 2026 } 2027 cp1 = new; 2028 *cp1 = '\0'; 2029 cp2 = mapout; 2030 while (*cp2) { 2031 match = 0; 2032 switch (*cp2) { 2033 case '\\': 2034 if (*(cp2 + 1)) { 2035 *cp1++ = *++cp2; 2036 } 2037 break; 2038 case '[': 2039 LOOP: 2040 if (*++cp2 == '$' && 2041 isdigit((unsigned char)*(cp2+1))) { 2042 if (*++cp2 == '0') { 2043 char *cp3 = name; 2044 2045 while (*cp3) { 2046 *cp1++ = *cp3++; 2047 } 2048 match = 1; 2049 } 2050 else if (toks[toknum = *cp2 - '1']) { 2051 char *cp3 = tp[toknum]; 2052 2053 while (cp3 != te[toknum]) { 2054 *cp1++ = *cp3++; 2055 } 2056 match = 1; 2057 } 2058 } 2059 else { 2060 while (*cp2 && *cp2 != ',' && 2061 *cp2 != ']') { 2062 if (*cp2 == '\\') { 2063 cp2++; 2064 } 2065 else if (*cp2 == '$' && 2066 isdigit((unsigned char)*(cp2+1))) { 2067 if (*++cp2 == '0') { 2068 char *cp3 = name; 2069 2070 while (*cp3) { 2071 *cp1++ = *cp3++; 2072 } 2073 } 2074 else if (toks[toknum = 2075 *cp2 - '1']) { 2076 char *cp3=tp[toknum]; 2077 2078 while (cp3 != 2079 te[toknum]) { 2080 *cp1++ = *cp3++; 2081 } 2082 } 2083 } 2084 else if (*cp2) { 2085 *cp1++ = *cp2++; 2086 } 2087 } 2088 if (!*cp2) { 2089 fputs( 2090 "nmap: unbalanced brackets.\n", 2091 ttyout); 2092 return (name); 2093 } 2094 match = 1; 2095 cp2--; 2096 } 2097 if (match) { 2098 while (*++cp2 && *cp2 != ']') { 2099 if (*cp2 == '\\' && *(cp2 + 1)) { 2100 cp2++; 2101 } 2102 } 2103 if (!*cp2) { 2104 fputs( 2105 "nmap: unbalanced brackets.\n", 2106 ttyout); 2107 return (name); 2108 } 2109 break; 2110 } 2111 switch (*++cp2) { 2112 case ',': 2113 goto LOOP; 2114 case ']': 2115 break; 2116 default: 2117 cp2--; 2118 goto LOOP; 2119 } 2120 break; 2121 case '$': 2122 if (isdigit((unsigned char)*(cp2 + 1))) { 2123 if (*++cp2 == '0') { 2124 char *cp3 = name; 2125 2126 while (*cp3) { 2127 *cp1++ = *cp3++; 2128 } 2129 } 2130 else if (toks[toknum = *cp2 - '1']) { 2131 char *cp3 = tp[toknum]; 2132 2133 while (cp3 != te[toknum]) { 2134 *cp1++ = *cp3++; 2135 } 2136 } 2137 break; 2138 } 2139 /* intentional drop through */ 2140 default: 2141 *cp1++ = *cp2; 2142 break; 2143 } 2144 cp2++; 2145 } 2146 *cp1 = '\0'; 2147 if (!*new) { 2148 return (name); 2149 } 2150 return (new); 2151 } 2152 2153 void 2154 setpassive(int argc, char *argv[]) 2155 { 2156 2157 if (argc == 1) { 2158 passivemode = !passivemode; 2159 activefallback = passivemode; 2160 } else if (argc != 2) { 2161 passiveusage: 2162 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]); 2163 code = -1; 2164 return; 2165 } else if (strcasecmp(argv[1], "on") == 0) { 2166 passivemode = 1; 2167 activefallback = 0; 2168 } else if (strcasecmp(argv[1], "off") == 0) { 2169 passivemode = 0; 2170 activefallback = 0; 2171 } else if (strcasecmp(argv[1], "auto") == 0) { 2172 passivemode = 1; 2173 activefallback = 1; 2174 } else 2175 goto passiveusage; 2176 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 2177 onoff(passivemode), onoff(activefallback)); 2178 code = passivemode; 2179 } 2180 2181 void 2182 setepsv4(int argc, char *argv[]) 2183 { 2184 2185 code = togglevar(argc, argv, &epsv4, 2186 verbose ? "EPSV/EPRT on IPv4" : NULL); 2187 epsv4bad = 0; 2188 } 2189 2190 void 2191 setsunique(int argc, char *argv[]) 2192 { 2193 2194 code = togglevar(argc, argv, &sunique, "Store unique"); 2195 } 2196 2197 void 2198 setrunique(int argc, char *argv[]) 2199 { 2200 2201 code = togglevar(argc, argv, &runique, "Receive unique"); 2202 } 2203 2204 int 2205 parserate(int argc, char *argv[], int cmdlineopt) 2206 { 2207 int dir, max, incr, showonly; 2208 sigfunc oldusr1, oldusr2; 2209 2210 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { 2211 usage: 2212 if (cmdlineopt) 2213 fprintf(ttyout, 2214 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", 2215 argv[0]); 2216 else 2217 fprintf(ttyout, 2218 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", 2219 argv[0]); 2220 return -1; 2221 } 2222 dir = max = incr = showonly = 0; 2223 #define RATE_GET 1 2224 #define RATE_PUT 2 2225 #define RATE_ALL (RATE_GET | RATE_PUT) 2226 2227 if (strcasecmp(argv[1], "all") == 0) 2228 dir = RATE_ALL; 2229 else if (strcasecmp(argv[1], "get") == 0) 2230 dir = RATE_GET; 2231 else if (strcasecmp(argv[1], "put") == 0) 2232 dir = RATE_PUT; 2233 else 2234 goto usage; 2235 2236 if (argc >= 3) { 2237 if ((max = strsuftoi(argv[2])) < 0) 2238 goto usage; 2239 } else 2240 showonly = 1; 2241 2242 if (argc == 4) { 2243 if ((incr = strsuftoi(argv[3])) <= 0) 2244 goto usage; 2245 } else 2246 incr = DEFAULTINCR; 2247 2248 oldusr1 = xsignal(SIGUSR1, SIG_IGN); 2249 oldusr2 = xsignal(SIGUSR2, SIG_IGN); 2250 if (dir & RATE_GET) { 2251 if (!showonly) { 2252 rate_get = max; 2253 rate_get_incr = incr; 2254 } 2255 if (!cmdlineopt || verbose) 2256 fprintf(ttyout, 2257 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 2258 onoff(rate_get), rate_get, rate_get_incr); 2259 } 2260 if (dir & RATE_PUT) { 2261 if (!showonly) { 2262 rate_put = max; 2263 rate_put_incr = incr; 2264 } 2265 if (!cmdlineopt || verbose) 2266 fprintf(ttyout, 2267 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 2268 onoff(rate_put), rate_put, rate_put_incr); 2269 } 2270 (void)xsignal(SIGUSR1, oldusr1); 2271 (void)xsignal(SIGUSR2, oldusr2); 2272 return 0; 2273 } 2274 2275 void 2276 setrate(int argc, char *argv[]) 2277 { 2278 2279 code = parserate(argc, argv, 0); 2280 } 2281 2282 /* change directory to parent directory */ 2283 void 2284 cdup(int argc, char *argv[]) 2285 { 2286 int r; 2287 2288 if (argc == 0) { 2289 fprintf(ttyout, "usage: %s\n", argv[0]); 2290 code = -1; 2291 return; 2292 } 2293 r = command("CDUP"); 2294 if (r == ERROR && code == 500) { 2295 if (verbose) 2296 fputs("CDUP command not recognized, trying XCUP.\n", 2297 ttyout); 2298 r = command("XCUP"); 2299 } 2300 if (r == COMPLETE) { 2301 dirchange = 1; 2302 updateremotepwd(); 2303 } 2304 } 2305 2306 /* 2307 * Restart transfer at specific point 2308 */ 2309 void 2310 restart(int argc, char *argv[]) 2311 { 2312 2313 if (argc == 0 || argc > 2) { 2314 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]); 2315 code = -1; 2316 return; 2317 } 2318 if (! features[FEAT_REST_STREAM]) { 2319 fprintf(ttyout, 2320 "Restart is not supported by the remote server.\n"); 2321 return; 2322 } 2323 if (argc == 2) { 2324 off_t rp; 2325 char *ep; 2326 2327 rp = STRTOLL(argv[1], &ep, 10); 2328 if (rp < 0 || *ep != '\0') 2329 fprintf(ttyout, "restart: Invalid offset `%s'\n", 2330 argv[1]); 2331 else 2332 restart_point = rp; 2333 } 2334 if (restart_point == 0) 2335 fputs("No restart point defined.\n", ttyout); 2336 else 2337 fprintf(ttyout, 2338 "Restarting at " LLF " for next get, put or append\n", 2339 (LLT)restart_point); 2340 } 2341 2342 /* 2343 * Show remote system type 2344 */ 2345 void 2346 syst(int argc, char *argv[]) 2347 { 2348 int oldverbose = verbose; 2349 2350 if (argc == 0) { 2351 fprintf(ttyout, "usage: %s\n", argv[0]); 2352 code = -1; 2353 return; 2354 } 2355 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2356 (void)command("SYST"); 2357 verbose = oldverbose; 2358 } 2359 2360 void 2361 macdef(int argc, char *argv[]) 2362 { 2363 char *tmp; 2364 int c; 2365 2366 if (argc == 0) 2367 goto usage; 2368 if (macnum == 16) { 2369 fputs("Limit of 16 macros have already been defined.\n", 2370 ttyout); 2371 code = -1; 2372 return; 2373 } 2374 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { 2375 usage: 2376 fprintf(ttyout, "usage: %s macro_name\n", argv[0]); 2377 code = -1; 2378 return; 2379 } 2380 if (interactive) 2381 fputs( 2382 "Enter macro line by line, terminating it with a null line.\n", 2383 ttyout); 2384 (void)strlcpy(macros[macnum].mac_name, argv[1], 2385 sizeof(macros[macnum].mac_name)); 2386 if (macnum == 0) 2387 macros[macnum].mac_start = macbuf; 2388 else 2389 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2390 tmp = macros[macnum].mac_start; 2391 while (tmp != macbuf+4096) { 2392 if ((c = getchar()) == EOF) { 2393 fputs("macdef: end of file encountered.\n", ttyout); 2394 code = -1; 2395 return; 2396 } 2397 if ((*tmp = c) == '\n') { 2398 if (tmp == macros[macnum].mac_start) { 2399 macros[macnum++].mac_end = tmp; 2400 code = 0; 2401 return; 2402 } 2403 if (*(tmp-1) == '\0') { 2404 macros[macnum++].mac_end = tmp - 1; 2405 code = 0; 2406 return; 2407 } 2408 *tmp = '\0'; 2409 } 2410 tmp++; 2411 } 2412 while (1) { 2413 while ((c = getchar()) != '\n' && c != EOF) 2414 /* LOOP */; 2415 if (c == EOF || getchar() == '\n') { 2416 fputs("Macro not defined - 4K buffer exceeded.\n", 2417 ttyout); 2418 code = -1; 2419 return; 2420 } 2421 } 2422 } 2423 2424 /* 2425 * Get size of file on remote machine 2426 */ 2427 void 2428 sizecmd(int argc, char *argv[]) 2429 { 2430 off_t size; 2431 2432 if (argc == 0 || argc > 2 || 2433 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2434 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 2435 code = -1; 2436 return; 2437 } 2438 size = remotesize(argv[1], 1); 2439 if (size != -1) 2440 fprintf(ttyout, 2441 "%s\t" LLF "\n", argv[1], (LLT)size); 2442 code = (size > 0); 2443 } 2444 2445 /* 2446 * Get last modification time of file on remote machine 2447 */ 2448 void 2449 modtime(int argc, char *argv[]) 2450 { 2451 time_t mtime; 2452 2453 if (argc == 0 || argc > 2 || 2454 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2455 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 2456 code = -1; 2457 return; 2458 } 2459 mtime = remotemodtime(argv[1], 1); 2460 if (mtime != -1) 2461 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); 2462 code = (mtime > 0); 2463 } 2464 2465 /* 2466 * Show status on remote machine 2467 */ 2468 void 2469 rmtstatus(int argc, char *argv[]) 2470 { 2471 2472 if (argc == 0) { 2473 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]); 2474 code = -1; 2475 return; 2476 } 2477 COMMAND_1ARG(argc, argv, "STAT"); 2478 } 2479 2480 /* 2481 * Get file if modtime is more recent than current file 2482 */ 2483 void 2484 newer(int argc, char *argv[]) 2485 { 2486 2487 if (getit(argc, argv, -1, "w")) 2488 fprintf(ttyout, 2489 "Local file \"%s\" is newer than remote file \"%s\".\n", 2490 argv[2], argv[1]); 2491 } 2492 2493 /* 2494 * Display one local file through $PAGER. 2495 */ 2496 void 2497 lpage(int argc, char *argv[]) 2498 { 2499 int len; 2500 char *p, *pager, *locfile; 2501 2502 if (argc == 0 || argc > 2 || 2503 (argc == 1 && !another(&argc, &argv, "local-file"))) { 2504 fprintf(ttyout, "usage: %s local-file\n", argv[0]); 2505 code = -1; 2506 return; 2507 } 2508 if ((locfile = globulize(argv[1])) == NULL) { 2509 code = -1; 2510 return; 2511 } 2512 p = getoptionvalue("pager"); 2513 if (EMPTYSTRING(p)) 2514 p = DEFAULTPAGER; 2515 len = strlen(p) + strlen(locfile) + 2; 2516 pager = xmalloc(len); 2517 (void)strlcpy(pager, p, len); 2518 (void)strlcat(pager, " ", len); 2519 (void)strlcat(pager, locfile, len); 2520 system(pager); 2521 code = 0; 2522 (void)free(pager); 2523 (void)free(locfile); 2524 } 2525 2526 /* 2527 * Display one remote file through $PAGER. 2528 */ 2529 void 2530 page(int argc, char *argv[]) 2531 { 2532 int ohash, orestart_point, overbose, len; 2533 char *p, *pager; 2534 2535 if (argc == 0 || argc > 2 || 2536 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2537 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 2538 code = -1; 2539 return; 2540 } 2541 p = getoptionvalue("pager"); 2542 if (EMPTYSTRING(p)) 2543 p = DEFAULTPAGER; 2544 len = strlen(p) + 2; 2545 pager = xmalloc(len); 2546 pager[0] = '|'; 2547 (void)strlcpy(pager + 1, p, len - 1); 2548 2549 ohash = hash; 2550 orestart_point = restart_point; 2551 overbose = verbose; 2552 hash = restart_point = verbose = 0; 2553 recvrequest("RETR", pager, argv[1], "r+", 1, 0); 2554 hash = ohash; 2555 restart_point = orestart_point; 2556 verbose = overbose; 2557 (void)free(pager); 2558 } 2559 2560 /* 2561 * Set the socket send or receive buffer size. 2562 */ 2563 void 2564 setxferbuf(int argc, char *argv[]) 2565 { 2566 int size, dir; 2567 2568 if (argc != 2) { 2569 usage: 2570 fprintf(ttyout, "usage: %s size\n", argv[0]); 2571 code = -1; 2572 return; 2573 } 2574 if (strcasecmp(argv[0], "sndbuf") == 0) 2575 dir = RATE_PUT; 2576 else if (strcasecmp(argv[0], "rcvbuf") == 0) 2577 dir = RATE_GET; 2578 else if (strcasecmp(argv[0], "xferbuf") == 0) 2579 dir = RATE_ALL; 2580 else 2581 goto usage; 2582 2583 if ((size = strsuftoi(argv[1])) == -1) 2584 goto usage; 2585 2586 if (size == 0) { 2587 fprintf(ttyout, "%s: size must be positive.\n", argv[0]); 2588 goto usage; 2589 } 2590 2591 if (dir & RATE_PUT) 2592 sndbuf_size = size; 2593 if (dir & RATE_GET) 2594 rcvbuf_size = size; 2595 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n", 2596 sndbuf_size, rcvbuf_size); 2597 code = 0; 2598 } 2599 2600 /* 2601 * Set or display options (defaults are provided by various env vars) 2602 */ 2603 void 2604 setoption(int argc, char *argv[]) 2605 { 2606 struct option *o; 2607 2608 code = -1; 2609 if (argc == 0 || (argc != 1 && argc != 3)) { 2610 fprintf(ttyout, "usage: %s [option value]\n", argv[0]); 2611 return; 2612 } 2613 2614 #define OPTIONINDENT ((int) sizeof("http_proxy")) 2615 if (argc == 1) { 2616 for (o = optiontab; o->name != NULL; o++) { 2617 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, 2618 o->name, o->value ? o->value : ""); 2619 } 2620 } else { 2621 o = getoption(argv[1]); 2622 if (o == NULL) { 2623 fprintf(ttyout, "No such option `%s'.\n", argv[1]); 2624 return; 2625 } 2626 FREEPTR(o->value); 2627 o->value = xstrdup(argv[2]); 2628 if (verbose) 2629 fprintf(ttyout, "Setting `%s' to `%s'.\n", 2630 o->name, o->value); 2631 } 2632 code = 0; 2633 } 2634 2635 /* 2636 * Unset an option 2637 */ 2638 void 2639 unsetoption(int argc, char *argv[]) 2640 { 2641 struct option *o; 2642 2643 code = -1; 2644 if (argc == 0 || argc != 2) { 2645 fprintf(ttyout, "usage: %s option\n", argv[0]); 2646 return; 2647 } 2648 2649 o = getoption(argv[1]); 2650 if (o == NULL) { 2651 fprintf(ttyout, "No such option `%s'.\n", argv[1]); 2652 return; 2653 } 2654 FREEPTR(o->value); 2655 fprintf(ttyout, "Unsetting `%s'.\n", o->name); 2656 code = 0; 2657 } 2658 2659 /* 2660 * Display features supported by the remote host. 2661 */ 2662 void 2663 feat(int argc, char *argv[]) 2664 { 2665 int oldverbose = verbose; 2666 2667 if (argc == 0) { 2668 fprintf(ttyout, "usage: %s\n", argv[0]); 2669 code = -1; 2670 return; 2671 } 2672 if (! features[FEAT_FEAT]) { 2673 fprintf(ttyout, 2674 "FEAT is not supported by the remote server.\n"); 2675 return; 2676 } 2677 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2678 (void)command("FEAT"); 2679 verbose = oldverbose; 2680 } 2681 2682 void 2683 mlst(int argc, char *argv[]) 2684 { 2685 int oldverbose = verbose; 2686 2687 if (argc < 1 || argc > 2) { 2688 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]); 2689 code = -1; 2690 return; 2691 } 2692 if (! features[FEAT_MLST]) { 2693 fprintf(ttyout, 2694 "MLST is not supported by the remote server.\n"); 2695 return; 2696 } 2697 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2698 COMMAND_1ARG(argc, argv, "MLST"); 2699 verbose = oldverbose; 2700 } 2701 2702 void 2703 opts(int argc, char *argv[]) 2704 { 2705 int oldverbose = verbose; 2706 2707 if (argc < 2 || argc > 3) { 2708 fprintf(ttyout, "usage: %s command [options]\n", argv[0]); 2709 code = -1; 2710 return; 2711 } 2712 if (! features[FEAT_FEAT]) { 2713 fprintf(ttyout, 2714 "OPTS is not supported by the remote server.\n"); 2715 return; 2716 } 2717 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2718 if (argc == 2) 2719 command("OPTS %s", argv[1]); 2720 else 2721 command("OPTS %s %s", argv[1], argv[2]); 2722 verbose = oldverbose; 2723 } 2724