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