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