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