1 /* $NetBSD: cmds.c,v 1.115 2005/06/10 00:18:46 lukem 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.115 2005/06/10 00:18:46 lukem 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 const char *errormsg; 162 char line[BUFSIZ]; 163 164 if (!interactive || confirmrest) 165 return (1); 166 while (1) { 167 fprintf(ttyout, "%s %s [anpqy?]? ", cmd, file); 168 (void)fflush(ttyout); 169 if (getline(stdin, line, sizeof(line), &errormsg) < 0) { 170 mflag = 0; 171 fprintf(ttyout, "%s; %s aborted\n", errormsg, mname); 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 const 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 size_t 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 *password; 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 password = getpass("Password: "); 1493 } else { 1494 password = argv[2]; 1495 } 1496 n = command("PASS %s", password); 1497 memset(password, 0, strlen(password)); 1498 } 1499 if (n == CONTINUE) { 1500 aflag++; 1501 if (argc < 4) { 1502 password = getpass("Account: "); 1503 } else { 1504 password = argv[3]; 1505 } 1506 n = command("ACCT %s", password); 1507 memset(password, 0, strlen(password)); 1508 } 1509 if (n != COMPLETE) { 1510 fputs("Login failed.\n", ttyout); 1511 return; 1512 } 1513 if (!aflag && argc == 4) { 1514 password = argv[3]; 1515 (void)command("ACCT %s", password); 1516 memset(password, 0, strlen(password)); 1517 } 1518 connected = -1; 1519 getremoteinfo(); 1520 } 1521 1522 /* 1523 * Print working directory on remote machine. 1524 */ 1525 /*VARARGS*/ 1526 void 1527 pwd(int argc, char *argv[]) 1528 { 1529 1530 code = -1; 1531 if (argc != 1) { 1532 fprintf(ttyout, "usage: %s\n", argv[0]); 1533 return; 1534 } 1535 if (! remotecwd[0]) 1536 updateremotecwd(); 1537 if (! remotecwd[0]) 1538 fprintf(ttyout, "Unable to determine remote directory\n"); 1539 else { 1540 fprintf(ttyout, "Remote directory: %s\n", remotecwd); 1541 code = 0; 1542 } 1543 } 1544 1545 /* 1546 * Print working directory on local machine. 1547 */ 1548 void 1549 lpwd(int argc, char *argv[]) 1550 { 1551 1552 code = -1; 1553 if (argc != 1) { 1554 fprintf(ttyout, "usage: %s\n", argv[0]); 1555 return; 1556 } 1557 if (! localcwd[0]) 1558 updatelocalcwd(); 1559 if (! localcwd[0]) 1560 fprintf(ttyout, "Unable to determine local directory\n"); 1561 else { 1562 fprintf(ttyout, "Local directory: %s\n", localcwd); 1563 code = 0; 1564 } 1565 } 1566 1567 /* 1568 * Make a directory. 1569 */ 1570 void 1571 makedir(int argc, char *argv[]) 1572 { 1573 int r; 1574 1575 if (argc == 0 || argc > 2 || 1576 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1577 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1578 code = -1; 1579 return; 1580 } 1581 r = command("MKD %s", argv[1]); 1582 if (r == ERROR && code == 500) { 1583 if (verbose) 1584 fputs("MKD command not recognized, trying XMKD.\n", 1585 ttyout); 1586 r = command("XMKD %s", argv[1]); 1587 } 1588 if (r == COMPLETE) 1589 dirchange = 1; 1590 } 1591 1592 /* 1593 * Remove a directory. 1594 */ 1595 void 1596 removedir(int argc, char *argv[]) 1597 { 1598 int r; 1599 1600 if (argc == 0 || argc > 2 || 1601 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1602 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1603 code = -1; 1604 return; 1605 } 1606 r = command("RMD %s", argv[1]); 1607 if (r == ERROR && code == 500) { 1608 if (verbose) 1609 fputs("RMD command not recognized, trying XRMD.\n", 1610 ttyout); 1611 r = command("XRMD %s", argv[1]); 1612 } 1613 if (r == COMPLETE) 1614 dirchange = 1; 1615 } 1616 1617 /* 1618 * Send a line, verbatim, to the remote machine. 1619 */ 1620 void 1621 quote(int argc, char *argv[]) 1622 { 1623 1624 if (argc == 0 || 1625 (argc == 1 && !another(&argc, &argv, "command line to send"))) { 1626 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]); 1627 code = -1; 1628 return; 1629 } 1630 quote1("", argc, argv); 1631 } 1632 1633 /* 1634 * Send a SITE command to the remote machine. The line 1635 * is sent verbatim to the remote machine, except that the 1636 * word "SITE" is added at the front. 1637 */ 1638 void 1639 site(int argc, char *argv[]) 1640 { 1641 1642 if (argc == 0 || 1643 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){ 1644 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]); 1645 code = -1; 1646 return; 1647 } 1648 quote1("SITE ", argc, argv); 1649 } 1650 1651 /* 1652 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1653 * Send the result as a one-line command and get response. 1654 */ 1655 void 1656 quote1(const char *initial, int argc, char *argv[]) 1657 { 1658 int i; 1659 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1660 1661 (void)strlcpy(buf, initial, sizeof(buf)); 1662 for (i = 1; i < argc; i++) { 1663 (void)strlcat(buf, argv[i], sizeof(buf)); 1664 if (i < (argc - 1)) 1665 (void)strlcat(buf, " ", sizeof(buf)); 1666 } 1667 if (command("%s", buf) == PRELIM) { 1668 while (getreply(0) == PRELIM) 1669 continue; 1670 } 1671 dirchange = 1; 1672 } 1673 1674 void 1675 do_chmod(int argc, char *argv[]) 1676 { 1677 1678 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode"))) 1679 goto usage; 1680 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 1681 usage: 1682 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]); 1683 code = -1; 1684 return; 1685 } 1686 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1687 } 1688 1689 #define COMMAND_1ARG(argc, argv, cmd) \ 1690 if (argc == 1) \ 1691 command(cmd); \ 1692 else \ 1693 command(cmd " %s", argv[1]) 1694 1695 void 1696 do_umask(int argc, char *argv[]) 1697 { 1698 int oldverbose = verbose; 1699 1700 if (argc == 0) { 1701 fprintf(ttyout, "usage: %s [umask]\n", argv[0]); 1702 code = -1; 1703 return; 1704 } 1705 verbose = 1; 1706 COMMAND_1ARG(argc, argv, "SITE UMASK"); 1707 verbose = oldverbose; 1708 } 1709 1710 void 1711 idlecmd(int argc, char *argv[]) 1712 { 1713 int oldverbose = verbose; 1714 1715 if (argc < 1 || argc > 2) { 1716 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]); 1717 code = -1; 1718 return; 1719 } 1720 verbose = 1; 1721 COMMAND_1ARG(argc, argv, "SITE IDLE"); 1722 verbose = oldverbose; 1723 } 1724 1725 /* 1726 * Ask the other side for help. 1727 */ 1728 void 1729 rmthelp(int argc, char *argv[]) 1730 { 1731 int oldverbose = verbose; 1732 1733 if (argc == 0) { 1734 fprintf(ttyout, "usage: %s\n", argv[0]); 1735 code = -1; 1736 return; 1737 } 1738 verbose = 1; 1739 COMMAND_1ARG(argc, argv, "HELP"); 1740 verbose = oldverbose; 1741 } 1742 1743 /* 1744 * Terminate session and exit. 1745 * May be called with 0, NULL. 1746 */ 1747 /*VARARGS*/ 1748 void 1749 quit(int argc, char *argv[]) 1750 { 1751 1752 /* this may be called with argc == 0, argv == NULL */ 1753 if (argc == 0 && argv != NULL) { 1754 fprintf(ttyout, "usage: %s\n", argv[0]); 1755 code = -1; 1756 return; 1757 } 1758 if (connected) 1759 disconnect(0, NULL); 1760 pswitch(1); 1761 if (connected) 1762 disconnect(0, NULL); 1763 exit(0); 1764 } 1765 1766 /* 1767 * Terminate session, but don't exit. 1768 * May be called with 0, NULL. 1769 */ 1770 void 1771 disconnect(int argc, char *argv[]) 1772 { 1773 1774 /* this may be called with argc == 0, argv == NULL */ 1775 if (argc == 0 && argv != NULL) { 1776 fprintf(ttyout, "usage: %s\n", argv[0]); 1777 code = -1; 1778 return; 1779 } 1780 if (!connected) 1781 return; 1782 (void)command("QUIT"); 1783 cleanuppeer(); 1784 } 1785 1786 void 1787 account(int argc, char *argv[]) 1788 { 1789 char *ap; 1790 1791 if (argc == 0 || argc > 2) { 1792 fprintf(ttyout, "usage: %s [password]\n", argv[0]); 1793 code = -1; 1794 return; 1795 } 1796 else if (argc == 2) 1797 ap = argv[1]; 1798 else 1799 ap = getpass("Account:"); 1800 (void)command("ACCT %s", ap); 1801 } 1802 1803 sigjmp_buf abortprox; 1804 1805 void 1806 proxabort(int notused) 1807 { 1808 1809 sigint_raised = 1; 1810 alarmtimer(0); 1811 if (!proxy) { 1812 pswitch(1); 1813 } 1814 if (connected) { 1815 proxflag = 1; 1816 } 1817 else { 1818 proxflag = 0; 1819 } 1820 pswitch(0); 1821 siglongjmp(abortprox, 1); 1822 } 1823 1824 void 1825 doproxy(int argc, char *argv[]) 1826 { 1827 struct cmd *c; 1828 int cmdpos; 1829 sigfunc oldintr; 1830 1831 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { 1832 fprintf(ttyout, "usage: %s command\n", argv[0]); 1833 code = -1; 1834 return; 1835 } 1836 c = getcmd(argv[1]); 1837 if (c == (struct cmd *) -1) { 1838 fputs("?Ambiguous command.\n", ttyout); 1839 code = -1; 1840 return; 1841 } 1842 if (c == 0) { 1843 fputs("?Invalid command.\n", ttyout); 1844 code = -1; 1845 return; 1846 } 1847 if (!c->c_proxy) { 1848 fputs("?Invalid proxy command.\n", ttyout); 1849 code = -1; 1850 return; 1851 } 1852 if (sigsetjmp(abortprox, 1)) { 1853 code = -1; 1854 return; 1855 } 1856 oldintr = xsignal(SIGINT, proxabort); 1857 pswitch(1); 1858 if (c->c_conn && !connected) { 1859 fputs("Not connected.\n", ttyout); 1860 pswitch(0); 1861 (void)xsignal(SIGINT, oldintr); 1862 code = -1; 1863 return; 1864 } 1865 cmdpos = strcspn(line, " \t"); 1866 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1867 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1868 argv[1] = c->c_name; 1869 (*c->c_handler)(argc-1, argv+1); 1870 if (connected) { 1871 proxflag = 1; 1872 } 1873 else { 1874 proxflag = 0; 1875 } 1876 pswitch(0); 1877 (void)xsignal(SIGINT, oldintr); 1878 } 1879 1880 void 1881 setcase(int argc, char *argv[]) 1882 { 1883 1884 code = togglevar(argc, argv, &mcase, "Case mapping"); 1885 } 1886 1887 /* 1888 * convert the given name to lower case if it's all upper case, into 1889 * a static buffer which is returned to the caller 1890 */ 1891 static const char * 1892 docase(char *dst, size_t dlen, const char *src) 1893 { 1894 size_t i; 1895 int dochange = 1; 1896 1897 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) { 1898 dst[i] = src[i]; 1899 if (islower((unsigned char)dst[i])) 1900 dochange = 0; 1901 } 1902 dst[i] = '\0'; 1903 1904 if (dochange) { 1905 for (i = 0; dst[i] != '\0'; i++) 1906 if (isupper((unsigned char)dst[i])) 1907 dst[i] = tolower((unsigned char)dst[i]); 1908 } 1909 return dst; 1910 } 1911 1912 void 1913 setcr(int argc, char *argv[]) 1914 { 1915 1916 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1917 } 1918 1919 void 1920 setntrans(int argc, char *argv[]) 1921 { 1922 1923 if (argc == 0 || argc > 3) { 1924 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]); 1925 code = -1; 1926 return; 1927 } 1928 if (argc == 1) { 1929 ntflag = 0; 1930 fputs("Ntrans off.\n", ttyout); 1931 code = ntflag; 1932 return; 1933 } 1934 ntflag++; 1935 code = ntflag; 1936 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1937 if (argc == 2) { 1938 ntout[0] = '\0'; 1939 return; 1940 } 1941 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1942 } 1943 1944 static const char * 1945 dotrans(char *dst, size_t dlen, const char *src) 1946 { 1947 const char *cp1; 1948 char *cp2 = dst; 1949 size_t i, ostop; 1950 1951 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 1952 continue; 1953 for (cp1 = src; *cp1; cp1++) { 1954 int found = 0; 1955 for (i = 0; *(ntin + i) && i < 16; i++) { 1956 if (*cp1 == *(ntin + i)) { 1957 found++; 1958 if (i < ostop) { 1959 *cp2++ = *(ntout + i); 1960 if (cp2 - dst >= dlen - 1) 1961 goto out; 1962 } 1963 break; 1964 } 1965 } 1966 if (!found) { 1967 *cp2++ = *cp1; 1968 } 1969 } 1970 out: 1971 *cp2 = '\0'; 1972 return dst; 1973 } 1974 1975 void 1976 setnmap(int argc, char *argv[]) 1977 { 1978 char *cp; 1979 1980 if (argc == 1) { 1981 mapflag = 0; 1982 fputs("Nmap off.\n", ttyout); 1983 code = mapflag; 1984 return; 1985 } 1986 if (argc == 0 || 1987 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { 1988 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]); 1989 code = -1; 1990 return; 1991 } 1992 mapflag = 1; 1993 code = 1; 1994 cp = strchr(altarg, ' '); 1995 if (proxy) { 1996 while(*++cp == ' ') 1997 continue; 1998 altarg = cp; 1999 cp = strchr(altarg, ' '); 2000 } 2001 *cp = '\0'; 2002 (void)strlcpy(mapin, altarg, MAXPATHLEN); 2003 while (*++cp == ' ') 2004 continue; 2005 (void)strlcpy(mapout, cp, MAXPATHLEN); 2006 } 2007 2008 static const char * 2009 domap(char *dst, size_t dlen, const char *src) 2010 { 2011 const char *cp1 = src; 2012 char *cp2 = mapin; 2013 const char *tp[9], *te[9]; 2014 int i, toks[9], toknum = 0, match = 1; 2015 2016 for (i=0; i < 9; ++i) { 2017 toks[i] = 0; 2018 } 2019 while (match && *cp1 && *cp2) { 2020 switch (*cp2) { 2021 case '\\': 2022 if (*++cp2 != *cp1) { 2023 match = 0; 2024 } 2025 break; 2026 case '$': 2027 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 2028 if (*cp1 != *(++cp2+1)) { 2029 toks[toknum = *cp2 - '1']++; 2030 tp[toknum] = cp1; 2031 while (*++cp1 && *(cp2+1) 2032 != *cp1); 2033 te[toknum] = cp1; 2034 } 2035 cp2++; 2036 break; 2037 } 2038 /* FALLTHROUGH */ 2039 default: 2040 if (*cp2 != *cp1) { 2041 match = 0; 2042 } 2043 break; 2044 } 2045 if (match && *cp1) { 2046 cp1++; 2047 } 2048 if (match && *cp2) { 2049 cp2++; 2050 } 2051 } 2052 if (!match && *cp1) /* last token mismatch */ 2053 { 2054 toks[toknum] = 0; 2055 } 2056 cp2 = dst; 2057 *cp2 = '\0'; 2058 cp1 = mapout; 2059 while (*cp1) { 2060 match = 0; 2061 switch (*cp1) { 2062 case '\\': 2063 if (*(cp1 + 1)) { 2064 *cp2++ = *++cp1; 2065 } 2066 break; 2067 case '[': 2068 LOOP: 2069 if (*++cp1 == '$' && 2070 isdigit((unsigned char)*(cp1+1))) { 2071 if (*++cp1 == '0') { 2072 const char *cp3 = src; 2073 2074 while (*cp3) { 2075 *cp2++ = *cp3++; 2076 } 2077 match = 1; 2078 } 2079 else if (toks[toknum = *cp1 - '1']) { 2080 const char *cp3 = tp[toknum]; 2081 2082 while (cp3 != te[toknum]) { 2083 *cp2++ = *cp3++; 2084 } 2085 match = 1; 2086 } 2087 } 2088 else { 2089 while (*cp1 && *cp1 != ',' && 2090 *cp1 != ']') { 2091 if (*cp1 == '\\') { 2092 cp1++; 2093 } 2094 else if (*cp1 == '$' && 2095 isdigit((unsigned char)*(cp1+1))) { 2096 if (*++cp1 == '0') { 2097 const char *cp3 = src; 2098 2099 while (*cp3) { 2100 *cp2++ = *cp3++; 2101 } 2102 } 2103 else if (toks[toknum = 2104 *cp1 - '1']) { 2105 const char *cp3=tp[toknum]; 2106 2107 while (cp3 != 2108 te[toknum]) { 2109 *cp2++ = *cp3++; 2110 } 2111 } 2112 } 2113 else if (*cp1) { 2114 *cp2++ = *cp1++; 2115 } 2116 } 2117 if (!*cp1) { 2118 fputs( 2119 "nmap: unbalanced brackets.\n", 2120 ttyout); 2121 return (src); 2122 } 2123 match = 1; 2124 cp1--; 2125 } 2126 if (match) { 2127 while (*++cp1 && *cp1 != ']') { 2128 if (*cp1 == '\\' && *(cp1 + 1)) { 2129 cp1++; 2130 } 2131 } 2132 if (!*cp1) { 2133 fputs( 2134 "nmap: unbalanced brackets.\n", 2135 ttyout); 2136 return (src); 2137 } 2138 break; 2139 } 2140 switch (*++cp1) { 2141 case ',': 2142 goto LOOP; 2143 case ']': 2144 break; 2145 default: 2146 cp1--; 2147 goto LOOP; 2148 } 2149 break; 2150 case '$': 2151 if (isdigit((unsigned char)*(cp1 + 1))) { 2152 if (*++cp1 == '0') { 2153 const char *cp3 = src; 2154 2155 while (*cp3) { 2156 *cp2++ = *cp3++; 2157 } 2158 } 2159 else if (toks[toknum = *cp1 - '1']) { 2160 const char *cp3 = tp[toknum]; 2161 2162 while (cp3 != te[toknum]) { 2163 *cp2++ = *cp3++; 2164 } 2165 } 2166 break; 2167 } 2168 /* intentional drop through */ 2169 default: 2170 *cp2++ = *cp1; 2171 break; 2172 } 2173 cp1++; 2174 } 2175 *cp2 = '\0'; 2176 return *dst ? dst : src; 2177 } 2178 2179 void 2180 setpassive(int argc, char *argv[]) 2181 { 2182 2183 if (argc == 1) { 2184 passivemode = !passivemode; 2185 activefallback = passivemode; 2186 } else if (argc != 2) { 2187 passiveusage: 2188 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]); 2189 code = -1; 2190 return; 2191 } else if (strcasecmp(argv[1], "on") == 0) { 2192 passivemode = 1; 2193 activefallback = 0; 2194 } else if (strcasecmp(argv[1], "off") == 0) { 2195 passivemode = 0; 2196 activefallback = 0; 2197 } else if (strcasecmp(argv[1], "auto") == 0) { 2198 passivemode = 1; 2199 activefallback = 1; 2200 } else 2201 goto passiveusage; 2202 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 2203 onoff(passivemode), onoff(activefallback)); 2204 code = passivemode; 2205 } 2206 2207 void 2208 setepsv4(int argc, char *argv[]) 2209 { 2210 2211 code = togglevar(argc, argv, &epsv4, 2212 verbose ? "EPSV/EPRT on IPv4" : NULL); 2213 epsv4bad = 0; 2214 } 2215 2216 void 2217 setsunique(int argc, char *argv[]) 2218 { 2219 2220 code = togglevar(argc, argv, &sunique, "Store unique"); 2221 } 2222 2223 void 2224 setrunique(int argc, char *argv[]) 2225 { 2226 2227 code = togglevar(argc, argv, &runique, "Receive unique"); 2228 } 2229 2230 int 2231 parserate(int argc, char *argv[], int cmdlineopt) 2232 { 2233 int dir, max, incr, showonly; 2234 sigfunc oldusr1, oldusr2; 2235 2236 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { 2237 usage: 2238 if (cmdlineopt) 2239 fprintf(ttyout, 2240 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", 2241 argv[0]); 2242 else 2243 fprintf(ttyout, 2244 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", 2245 argv[0]); 2246 return -1; 2247 } 2248 dir = max = incr = showonly = 0; 2249 #define RATE_GET 1 2250 #define RATE_PUT 2 2251 #define RATE_ALL (RATE_GET | RATE_PUT) 2252 2253 if (strcasecmp(argv[1], "all") == 0) 2254 dir = RATE_ALL; 2255 else if (strcasecmp(argv[1], "get") == 0) 2256 dir = RATE_GET; 2257 else if (strcasecmp(argv[1], "put") == 0) 2258 dir = RATE_PUT; 2259 else 2260 goto usage; 2261 2262 if (argc >= 3) { 2263 if ((max = strsuftoi(argv[2])) < 0) 2264 goto usage; 2265 } else 2266 showonly = 1; 2267 2268 if (argc == 4) { 2269 if ((incr = strsuftoi(argv[3])) <= 0) 2270 goto usage; 2271 } else 2272 incr = DEFAULTINCR; 2273 2274 oldusr1 = xsignal(SIGUSR1, SIG_IGN); 2275 oldusr2 = xsignal(SIGUSR2, SIG_IGN); 2276 if (dir & RATE_GET) { 2277 if (!showonly) { 2278 rate_get = max; 2279 rate_get_incr = incr; 2280 } 2281 if (!cmdlineopt || verbose) 2282 fprintf(ttyout, 2283 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 2284 onoff(rate_get), rate_get, rate_get_incr); 2285 } 2286 if (dir & RATE_PUT) { 2287 if (!showonly) { 2288 rate_put = max; 2289 rate_put_incr = incr; 2290 } 2291 if (!cmdlineopt || verbose) 2292 fprintf(ttyout, 2293 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 2294 onoff(rate_put), rate_put, rate_put_incr); 2295 } 2296 (void)xsignal(SIGUSR1, oldusr1); 2297 (void)xsignal(SIGUSR2, oldusr2); 2298 return 0; 2299 } 2300 2301 void 2302 setrate(int argc, char *argv[]) 2303 { 2304 2305 code = parserate(argc, argv, 0); 2306 } 2307 2308 /* change directory to parent directory */ 2309 void 2310 cdup(int argc, char *argv[]) 2311 { 2312 int r; 2313 2314 if (argc == 0) { 2315 fprintf(ttyout, "usage: %s\n", argv[0]); 2316 code = -1; 2317 return; 2318 } 2319 r = command("CDUP"); 2320 if (r == ERROR && code == 500) { 2321 if (verbose) 2322 fputs("CDUP command not recognized, trying XCUP.\n", 2323 ttyout); 2324 r = command("XCUP"); 2325 } 2326 if (r == COMPLETE) { 2327 dirchange = 1; 2328 updateremotecwd(); 2329 } 2330 } 2331 2332 /* 2333 * Restart transfer at specific point 2334 */ 2335 void 2336 restart(int argc, char *argv[]) 2337 { 2338 2339 if (argc == 0 || argc > 2) { 2340 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]); 2341 code = -1; 2342 return; 2343 } 2344 if (! features[FEAT_REST_STREAM]) { 2345 fprintf(ttyout, 2346 "Restart is not supported by the remote server.\n"); 2347 return; 2348 } 2349 if (argc == 2) { 2350 off_t rp; 2351 char *ep; 2352 2353 rp = STRTOLL(argv[1], &ep, 10); 2354 if (rp < 0 || *ep != '\0') 2355 fprintf(ttyout, "restart: Invalid offset `%s'\n", 2356 argv[1]); 2357 else 2358 restart_point = rp; 2359 } 2360 if (restart_point == 0) 2361 fputs("No restart point defined.\n", ttyout); 2362 else 2363 fprintf(ttyout, 2364 "Restarting at " LLF " for next get, put or append\n", 2365 (LLT)restart_point); 2366 } 2367 2368 /* 2369 * Show remote system type 2370 */ 2371 void 2372 syst(int argc, char *argv[]) 2373 { 2374 int oldverbose = verbose; 2375 2376 if (argc == 0) { 2377 fprintf(ttyout, "usage: %s\n", argv[0]); 2378 code = -1; 2379 return; 2380 } 2381 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2382 (void)command("SYST"); 2383 verbose = oldverbose; 2384 } 2385 2386 void 2387 macdef(int argc, char *argv[]) 2388 { 2389 char *tmp; 2390 int c; 2391 2392 if (argc == 0) 2393 goto usage; 2394 if (macnum == 16) { 2395 fputs("Limit of 16 macros have already been defined.\n", 2396 ttyout); 2397 code = -1; 2398 return; 2399 } 2400 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { 2401 usage: 2402 fprintf(ttyout, "usage: %s macro_name\n", argv[0]); 2403 code = -1; 2404 return; 2405 } 2406 if (interactive) 2407 fputs( 2408 "Enter macro line by line, terminating it with a null line.\n", 2409 ttyout); 2410 (void)strlcpy(macros[macnum].mac_name, argv[1], 2411 sizeof(macros[macnum].mac_name)); 2412 if (macnum == 0) 2413 macros[macnum].mac_start = macbuf; 2414 else 2415 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2416 tmp = macros[macnum].mac_start; 2417 while (tmp != macbuf+4096) { 2418 if ((c = getchar()) == EOF) { 2419 fputs("macdef: end of file encountered.\n", ttyout); 2420 code = -1; 2421 return; 2422 } 2423 if ((*tmp = c) == '\n') { 2424 if (tmp == macros[macnum].mac_start) { 2425 macros[macnum++].mac_end = tmp; 2426 code = 0; 2427 return; 2428 } 2429 if (*(tmp-1) == '\0') { 2430 macros[macnum++].mac_end = tmp - 1; 2431 code = 0; 2432 return; 2433 } 2434 *tmp = '\0'; 2435 } 2436 tmp++; 2437 } 2438 while (1) { 2439 while ((c = getchar()) != '\n' && c != EOF) 2440 /* LOOP */; 2441 if (c == EOF || getchar() == '\n') { 2442 fputs("Macro not defined - 4K buffer exceeded.\n", 2443 ttyout); 2444 code = -1; 2445 return; 2446 } 2447 } 2448 } 2449 2450 /* 2451 * Get size of file on remote machine 2452 */ 2453 void 2454 sizecmd(int argc, char *argv[]) 2455 { 2456 off_t size; 2457 2458 if (argc == 0 || argc > 2 || 2459 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2460 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 2461 code = -1; 2462 return; 2463 } 2464 size = remotesize(argv[1], 1); 2465 if (size != -1) 2466 fprintf(ttyout, 2467 "%s\t" LLF "\n", argv[1], (LLT)size); 2468 code = (size > 0); 2469 } 2470 2471 /* 2472 * Get last modification time of file on remote machine 2473 */ 2474 void 2475 modtime(int argc, char *argv[]) 2476 { 2477 time_t mtime; 2478 2479 if (argc == 0 || argc > 2 || 2480 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2481 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 2482 code = -1; 2483 return; 2484 } 2485 mtime = remotemodtime(argv[1], 1); 2486 if (mtime != -1) 2487 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); 2488 code = (mtime > 0); 2489 } 2490 2491 /* 2492 * Show status on remote machine 2493 */ 2494 void 2495 rmtstatus(int argc, char *argv[]) 2496 { 2497 2498 if (argc == 0) { 2499 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]); 2500 code = -1; 2501 return; 2502 } 2503 COMMAND_1ARG(argc, argv, "STAT"); 2504 } 2505 2506 /* 2507 * Get file if modtime is more recent than current file 2508 */ 2509 void 2510 newer(int argc, char *argv[]) 2511 { 2512 2513 if (getit(argc, argv, -1, "w")) 2514 fprintf(ttyout, 2515 "Local file \"%s\" is newer than remote file \"%s\".\n", 2516 argv[2], argv[1]); 2517 } 2518 2519 /* 2520 * Display one local file through $PAGER. 2521 */ 2522 void 2523 lpage(int argc, char *argv[]) 2524 { 2525 size_t len; 2526 char *p, *pager, *locfile; 2527 2528 if (argc == 0 || argc > 2 || 2529 (argc == 1 && !another(&argc, &argv, "local-file"))) { 2530 fprintf(ttyout, "usage: %s local-file\n", argv[0]); 2531 code = -1; 2532 return; 2533 } 2534 if ((locfile = globulize(argv[1])) == NULL) { 2535 code = -1; 2536 return; 2537 } 2538 p = getoptionvalue("pager"); 2539 if (EMPTYSTRING(p)) 2540 p = DEFAULTPAGER; 2541 len = strlen(p) + strlen(locfile) + 2; 2542 pager = xmalloc(len); 2543 (void)strlcpy(pager, p, len); 2544 (void)strlcat(pager, " ", len); 2545 (void)strlcat(pager, locfile, len); 2546 system(pager); 2547 code = 0; 2548 (void)free(pager); 2549 (void)free(locfile); 2550 } 2551 2552 /* 2553 * Display one remote file through $PAGER. 2554 */ 2555 void 2556 page(int argc, char *argv[]) 2557 { 2558 int ohash, orestart_point, overbose; 2559 size_t len; 2560 char *p, *pager; 2561 2562 if (argc == 0 || argc > 2 || 2563 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2564 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 2565 code = -1; 2566 return; 2567 } 2568 p = getoptionvalue("pager"); 2569 if (EMPTYSTRING(p)) 2570 p = DEFAULTPAGER; 2571 len = strlen(p) + 2; 2572 pager = xmalloc(len); 2573 pager[0] = '|'; 2574 (void)strlcpy(pager + 1, p, len - 1); 2575 2576 ohash = hash; 2577 orestart_point = restart_point; 2578 overbose = verbose; 2579 hash = restart_point = verbose = 0; 2580 recvrequest("RETR", pager, argv[1], "r+", 1, 0); 2581 hash = ohash; 2582 restart_point = orestart_point; 2583 verbose = overbose; 2584 (void)free(pager); 2585 } 2586 2587 /* 2588 * Set the socket send or receive buffer size. 2589 */ 2590 void 2591 setxferbuf(int argc, char *argv[]) 2592 { 2593 int size, dir; 2594 2595 if (argc != 2) { 2596 usage: 2597 fprintf(ttyout, "usage: %s size\n", argv[0]); 2598 code = -1; 2599 return; 2600 } 2601 if (strcasecmp(argv[0], "sndbuf") == 0) 2602 dir = RATE_PUT; 2603 else if (strcasecmp(argv[0], "rcvbuf") == 0) 2604 dir = RATE_GET; 2605 else if (strcasecmp(argv[0], "xferbuf") == 0) 2606 dir = RATE_ALL; 2607 else 2608 goto usage; 2609 2610 if ((size = strsuftoi(argv[1])) == -1) 2611 goto usage; 2612 2613 if (size == 0) { 2614 fprintf(ttyout, "%s: size must be positive.\n", argv[0]); 2615 goto usage; 2616 } 2617 2618 if (dir & RATE_PUT) 2619 sndbuf_size = size; 2620 if (dir & RATE_GET) 2621 rcvbuf_size = size; 2622 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n", 2623 sndbuf_size, rcvbuf_size); 2624 code = 0; 2625 } 2626 2627 /* 2628 * Set or display options (defaults are provided by various env vars) 2629 */ 2630 void 2631 setoption(int argc, char *argv[]) 2632 { 2633 struct option *o; 2634 2635 code = -1; 2636 if (argc == 0 || (argc != 1 && argc != 3)) { 2637 fprintf(ttyout, "usage: %s [option value]\n", argv[0]); 2638 return; 2639 } 2640 2641 #define OPTIONINDENT ((int) sizeof("http_proxy")) 2642 if (argc == 1) { 2643 for (o = optiontab; o->name != NULL; o++) { 2644 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, 2645 o->name, o->value ? o->value : ""); 2646 } 2647 } else { 2648 o = getoption(argv[1]); 2649 if (o == NULL) { 2650 fprintf(ttyout, "No such option `%s'.\n", argv[1]); 2651 return; 2652 } 2653 FREEPTR(o->value); 2654 o->value = xstrdup(argv[2]); 2655 if (verbose) 2656 fprintf(ttyout, "Setting `%s' to `%s'.\n", 2657 o->name, o->value); 2658 } 2659 code = 0; 2660 } 2661 2662 /* 2663 * Unset an option 2664 */ 2665 void 2666 unsetoption(int argc, char *argv[]) 2667 { 2668 struct option *o; 2669 2670 code = -1; 2671 if (argc == 0 || argc != 2) { 2672 fprintf(ttyout, "usage: %s option\n", argv[0]); 2673 return; 2674 } 2675 2676 o = getoption(argv[1]); 2677 if (o == NULL) { 2678 fprintf(ttyout, "No such option `%s'.\n", argv[1]); 2679 return; 2680 } 2681 FREEPTR(o->value); 2682 fprintf(ttyout, "Unsetting `%s'.\n", o->name); 2683 code = 0; 2684 } 2685 2686 /* 2687 * Display features supported by the remote host. 2688 */ 2689 void 2690 feat(int argc, char *argv[]) 2691 { 2692 int oldverbose = verbose; 2693 2694 if (argc == 0) { 2695 fprintf(ttyout, "usage: %s\n", argv[0]); 2696 code = -1; 2697 return; 2698 } 2699 if (! features[FEAT_FEAT]) { 2700 fprintf(ttyout, 2701 "FEAT is not supported by the remote server.\n"); 2702 return; 2703 } 2704 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2705 (void)command("FEAT"); 2706 verbose = oldverbose; 2707 } 2708 2709 void 2710 mlst(int argc, char *argv[]) 2711 { 2712 int oldverbose = verbose; 2713 2714 if (argc < 1 || argc > 2) { 2715 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]); 2716 code = -1; 2717 return; 2718 } 2719 if (! features[FEAT_MLST]) { 2720 fprintf(ttyout, 2721 "MLST is not supported by the remote server.\n"); 2722 return; 2723 } 2724 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2725 COMMAND_1ARG(argc, argv, "MLST"); 2726 verbose = oldverbose; 2727 } 2728 2729 void 2730 opts(int argc, char *argv[]) 2731 { 2732 int oldverbose = verbose; 2733 2734 if (argc < 2 || argc > 3) { 2735 fprintf(ttyout, "usage: %s command [options]\n", argv[0]); 2736 code = -1; 2737 return; 2738 } 2739 if (! features[FEAT_FEAT]) { 2740 fprintf(ttyout, 2741 "OPTS is not supported by the remote server.\n"); 2742 return; 2743 } 2744 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2745 if (argc == 2) 2746 command("OPTS %s", argv[1]); 2747 else 2748 command("OPTS %s %s", argv[1], argv[2]); 2749 verbose = oldverbose; 2750 } 2751