1 /* $NetBSD: cmds.c,v 1.144 2024/10/04 18:05:43 christos 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.144 2024/10/04 18:05:43 christos 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 char *doprocess(char *, size_t, char *, int, int, int); 151 static char *domap(char *, size_t, const char *); 152 static char *docase(char *, size_t, const char *); 153 static 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 for (;;) { 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 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 char * 441 doprocess(char *dst, size_t dlen, 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, *locfile; 580 char buf[MAXPATHLEN]; 581 582 loc = rval = 0; 583 if (argc == 2) { 584 argc++; 585 argv[2] = argv[1]; 586 loc++; 587 } 588 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file"))) 589 goto usage; 590 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { 591 usage: 592 UPRINTF("usage: %s remote-file [local-file]\n", argv[0]); 593 code = -1; 594 return (0); 595 } 596 remfile = argv[1]; 597 if ((olocfile = globulize(argv[2])) == NULL) { 598 code = -1; 599 return (0); 600 } 601 locfile = doprocess(buf, sizeof(buf), olocfile, 602 loc && mcase, loc && ntflag, loc && mapflag); 603 if (restartit) { 604 struct stat stbuf; 605 int ret; 606 607 if (! features[FEAT_REST_STREAM]) { 608 fprintf(ttyout, 609 "Restart is not supported by the remote server.\n"); 610 return (0); 611 } 612 ret = stat(locfile, &stbuf); 613 if (restartit == 1) { 614 if (ret < 0) { 615 if (errno != ENOENT) { 616 warn("Can't stat `%s'", locfile); 617 goto freegetit; 618 } 619 restart_point = 0; 620 } 621 else 622 restart_point = stbuf.st_size; 623 } else { 624 if (ret == 0) { 625 time_t mtime; 626 627 mtime = remotemodtime(argv[1], 0); 628 if (mtime == -1) 629 goto freegetit; 630 if (stbuf.st_mtime >= mtime) { 631 rval = 1; 632 goto freegetit; 633 } 634 } 635 } 636 } 637 638 recvrequest("RETR", locfile, remfile, gmode, 639 remfile != argv[1] || locfile != argv[2], loc); 640 restart_point = 0; 641 freegetit: 642 (void)free(olocfile); 643 return (rval); 644 } 645 646 /* ARGSUSED */ 647 static void 648 mintr(int signo) 649 { 650 651 alarmtimer(0); 652 if (fromatty) 653 write(fileno(ttyout), "\n", 1); 654 siglongjmp(jabort, 1); 655 } 656 657 static void 658 mabort(const char *cmd) 659 { 660 int ointer, oconf; 661 662 if (mflag && fromatty) { 663 ointer = interactive; 664 oconf = confirmrest; 665 interactive = 1; 666 confirmrest = 0; 667 if (confirm(cmd, NULL)) { 668 interactive = ointer; 669 confirmrest = oconf; 670 return; 671 } 672 interactive = ointer; 673 confirmrest = oconf; 674 } 675 mflag = 0; 676 } 677 678 /* 679 * Get multiple files. 680 */ 681 void 682 mget(int argc, char *argv[]) 683 { 684 sigfunc oldintr; 685 int ointer; 686 char *cp, *tp; 687 int volatile restartit; 688 689 if (argc == 0 || 690 (argc == 1 && !another(&argc, &argv, "remote-files"))) { 691 UPRINTF("usage: %s remote-files\n", argv[0]); 692 code = -1; 693 return; 694 } 695 mflag = 1; 696 restart_point = 0; 697 restartit = 0; 698 if (strcmp(argv[0], "mreget") == 0) { 699 if (! features[FEAT_REST_STREAM]) { 700 fprintf(ttyout, 701 "Restart is not supported by the remote server.\n"); 702 return; 703 } 704 restartit = 1; 705 } 706 oldintr = xsignal(SIGINT, mintr); 707 if (sigsetjmp(jabort, 1)) 708 mabort(argv[0]); 709 while ((cp = remglob(argv, proxy, NULL)) != NULL) { 710 char buf[MAXPATHLEN]; 711 if (*cp == '\0' || !connected) { 712 mflag = 0; 713 continue; 714 } 715 if (! mflag) 716 continue; 717 if (! fileindir(cp, localcwd)) { 718 fprintf(ttyout, "Skipping non-relative filename `%s'\n", 719 cp); 720 continue; 721 } 722 if (!confirm(argv[0], cp)) 723 continue; 724 tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag); 725 if (restartit) { 726 struct stat stbuf; 727 728 if (stat(tp, &stbuf) == 0) 729 restart_point = stbuf.st_size; 730 else 731 warn("Can't stat `%s'", tp); 732 } 733 recvrequest("RETR", tp, cp, restart_point ? "r+" : "w", 734 tp != cp || !interactive, 1); 735 restart_point = 0; 736 if (!mflag && fromatty) { 737 ointer = interactive; 738 interactive = 1; 739 if (confirm(argv[0], NULL)) 740 mflag++; 741 interactive = ointer; 742 } 743 } 744 (void)xsignal(SIGINT, oldintr); 745 mflag = 0; 746 } 747 748 /* 749 * Read list of filenames from a local file and get those 750 */ 751 void 752 fget(int argc, char *argv[]) 753 { 754 const char *gmode; 755 FILE *fp; 756 char buf[MAXPATHLEN], cmdbuf[MAX_C_NAME]; 757 758 if (argc != 2) { 759 UPRINTF("usage: %s localfile\n", argv[0]); 760 code = -1; 761 return; 762 } 763 764 fp = fopen(argv[1], "r"); 765 if (fp == NULL) { 766 fprintf(ttyout, "Can't open source file %s\n", argv[1]); 767 code = -1; 768 return; 769 } 770 771 (void)strlcpy(cmdbuf, "get", sizeof(cmdbuf)); 772 argv[0] = cmdbuf; 773 gmode = restart_point ? "r+" : "w"; 774 775 while (get_line(fp, buf, sizeof(buf), NULL) >= 0) { 776 if (buf[0] == '\0') 777 continue; 778 argv[1] = buf; 779 (void)getit(argc, argv, 0, gmode); 780 } 781 fclose(fp); 782 } 783 784 const char * 785 onoff(int val) 786 { 787 788 return (val ? "on" : "off"); 789 } 790 791 /* 792 * Show status. 793 */ 794 /*ARGSUSED*/ 795 void 796 status(int argc, char *argv[]) 797 { 798 799 if (argc == 0) { 800 UPRINTF("usage: %s\n", argv[0]); 801 code = -1; 802 return; 803 } 804 #ifndef NO_STATUS 805 if (connected) 806 fprintf(ttyout, "Connected %sto %s.\n", 807 connected == -1 ? "and logged in" : "", hostname); 808 else 809 fputs("Not connected.\n", ttyout); 810 if (!proxy) { 811 pswitch(1); 812 if (connected) { 813 fprintf(ttyout, "Connected for proxy commands to %s.\n", 814 hostname); 815 } 816 else { 817 fputs("No proxy connection.\n", ttyout); 818 } 819 pswitch(0); 820 } 821 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), 822 *gateserver ? gateserver : "(none)", gateport); 823 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 824 onoff(passivemode), onoff(activefallback)); 825 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", 826 modename, typename, formname, structname); 827 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", 828 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob)); 829 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", 830 onoff(sunique), onoff(runique)); 831 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); 832 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), 833 onoff(crflag)); 834 if (ntflag) { 835 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); 836 } 837 else { 838 fputs("Ntrans: off.\n", ttyout); 839 } 840 if (mapflag) { 841 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); 842 } 843 else { 844 fputs("Nmap: off.\n", ttyout); 845 } 846 fprintf(ttyout, 847 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", 848 onoff(hash), mark, onoff(progress)); 849 fprintf(ttyout, 850 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 851 onoff(rate_get), rate_get, rate_get_incr); 852 fprintf(ttyout, 853 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 854 onoff(rate_put), rate_put, rate_put_incr); 855 fprintf(ttyout, 856 "Socket buffer sizes: send %d, receive %d.\n", 857 sndbuf_size, rcvbuf_size); 858 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport)); 859 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), 860 epsv4bad ? " (disabled for this connection)" : ""); 861 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv6: %s%s.\n", onoff(epsv6), 862 epsv6bad ? " (disabled for this connection)" : ""); 863 fprintf(ttyout, "Command line editing: %s.\n", 864 #ifdef NO_EDITCOMPLETE 865 "support not compiled in" 866 #else /* !def NO_EDITCOMPLETE */ 867 onoff(editing) 868 #endif /* !def NO_EDITCOMPLETE */ 869 ); 870 if (macnum > 0) { 871 int i; 872 873 fputs("Macros:\n", ttyout); 874 for (i=0; i<macnum; i++) { 875 fprintf(ttyout, "\t%s\n", macros[i].mac_name); 876 } 877 } 878 #endif /* !def NO_STATUS */ 879 fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION); 880 code = 0; 881 } 882 883 /* 884 * Toggle a variable 885 */ 886 int 887 togglevar(int argc, char *argv[], int *var, const char *mesg) 888 { 889 if (argc == 1) { 890 *var = !*var; 891 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { 892 *var = 1; 893 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { 894 *var = 0; 895 } else { 896 UPRINTF("usage: %s [ on | off ]\n", argv[0]); 897 return (-1); 898 } 899 if (mesg) 900 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); 901 return (*var); 902 } 903 904 /* 905 * Set beep on cmd completed mode. 906 */ 907 /*VARARGS*/ 908 void 909 setbell(int argc, char *argv[]) 910 { 911 912 code = togglevar(argc, argv, &bell, "Bell mode"); 913 } 914 915 /* 916 * Set command line editing 917 */ 918 /*VARARGS*/ 919 void 920 setedit(int argc, char *argv[]) 921 { 922 923 #ifdef NO_EDITCOMPLETE 924 if (argc == 0) { 925 UPRINTF("usage: %s\n", argv[0]); 926 code = -1; 927 return; 928 } 929 if (verbose) 930 fputs("Editing support not compiled in; ignoring command.\n", 931 ttyout); 932 #else /* !def NO_EDITCOMPLETE */ 933 code = togglevar(argc, argv, &editing, "Editing mode"); 934 controlediting(); 935 #endif /* !def NO_EDITCOMPLETE */ 936 } 937 938 /* 939 * Turn on packet tracing. 940 */ 941 /*VARARGS*/ 942 void 943 settrace(int argc, char *argv[]) 944 { 945 946 code = togglevar(argc, argv, &trace, "Packet tracing"); 947 } 948 949 /* 950 * Toggle hash mark printing during transfers, or set hash mark bytecount. 951 */ 952 /*VARARGS*/ 953 void 954 sethash(int argc, char *argv[]) 955 { 956 if (argc == 1) 957 hash = !hash; 958 else if (argc != 2) { 959 UPRINTF("usage: %s [ on | off | bytecount ]\n", 960 argv[0]); 961 code = -1; 962 return; 963 } else if (strcasecmp(argv[1], "on") == 0) 964 hash = 1; 965 else if (strcasecmp(argv[1], "off") == 0) 966 hash = 0; 967 else { 968 int nmark; 969 970 nmark = strsuftoi(argv[1]); 971 if (nmark < 1) { 972 fprintf(ttyout, "mark: bad bytecount value `%s'.\n", 973 argv[1]); 974 code = -1; 975 return; 976 } 977 mark = nmark; 978 hash = 1; 979 } 980 fprintf(ttyout, "Hash mark printing %s", onoff(hash)); 981 if (hash) 982 fprintf(ttyout, " (%d bytes/hash mark)", mark); 983 fputs(".\n", ttyout); 984 if (hash) 985 progress = 0; 986 code = hash; 987 } 988 989 /* 990 * Turn on printing of server echo's. 991 */ 992 /*VARARGS*/ 993 void 994 setverbose(int argc, char *argv[]) 995 { 996 997 code = togglevar(argc, argv, &verbose, "Verbose mode"); 998 } 999 1000 /* 1001 * Toggle PORT/LPRT cmd use before each data connection. 1002 */ 1003 /*VARARGS*/ 1004 void 1005 setport(int argc, char *argv[]) 1006 { 1007 1008 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); 1009 } 1010 1011 /* 1012 * Toggle transfer progress bar. 1013 */ 1014 /*VARARGS*/ 1015 void 1016 setprogress(int argc, char *argv[]) 1017 { 1018 1019 code = togglevar(argc, argv, &progress, "Progress bar"); 1020 if (progress) 1021 hash = 0; 1022 } 1023 1024 /* 1025 * Turn on interactive prompting during mget, mput, and mdelete. 1026 */ 1027 /*VARARGS*/ 1028 void 1029 setprompt(int argc, char *argv[]) 1030 { 1031 1032 code = togglevar(argc, argv, &interactive, "Interactive mode"); 1033 } 1034 1035 /* 1036 * Toggle gate-ftp mode, or set gate-ftp server 1037 */ 1038 /*VARARGS*/ 1039 void 1040 setgate(int argc, char *argv[]) 1041 { 1042 static char gsbuf[MAXHOSTNAMELEN]; 1043 1044 if (argc == 0 || argc > 3) { 1045 UPRINTF( 1046 "usage: %s [ on | off | gateserver [port] ]\n", argv[0]); 1047 code = -1; 1048 return; 1049 } else if (argc < 2) { 1050 gatemode = !gatemode; 1051 } else { 1052 if (argc == 2 && strcasecmp(argv[1], "on") == 0) 1053 gatemode = 1; 1054 else if (argc == 2 && strcasecmp(argv[1], "off") == 0) 1055 gatemode = 0; 1056 else { 1057 if (argc == 3) 1058 gateport = ftp_strdup(argv[2]); 1059 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf)); 1060 gateserver = gsbuf; 1061 gatemode = 1; 1062 } 1063 } 1064 if (gatemode && (gateserver == NULL || *gateserver == '\0')) { 1065 fprintf(ttyout, 1066 "Disabling gate-ftp mode - no gate-ftp server defined.\n"); 1067 gatemode = 0; 1068 } else { 1069 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", 1070 onoff(gatemode), *gateserver ? gateserver : "(none)", 1071 gateport); 1072 } 1073 code = gatemode; 1074 } 1075 1076 /* 1077 * Toggle metacharacter interpretation on local file names. 1078 */ 1079 /*VARARGS*/ 1080 void 1081 setglob(int argc, char *argv[]) 1082 { 1083 1084 code = togglevar(argc, argv, &doglob, "Globbing"); 1085 } 1086 1087 /* 1088 * Toggle preserving modification times on retrieved files. 1089 */ 1090 /*VARARGS*/ 1091 void 1092 setpreserve(int argc, char *argv[]) 1093 { 1094 1095 code = togglevar(argc, argv, &preserve, "Preserve modification times"); 1096 } 1097 1098 /* 1099 * Set debugging mode on/off and/or set level of debugging. 1100 */ 1101 /*VARARGS*/ 1102 void 1103 setdebug(int argc, char *argv[]) 1104 { 1105 if (argc == 0 || argc > 2) { 1106 UPRINTF("usage: %s [ on | off | debuglevel ]\n", argv[0]); 1107 code = -1; 1108 return; 1109 } else if (argc == 2) { 1110 if (strcasecmp(argv[1], "on") == 0) 1111 ftp_debug = 1; 1112 else if (strcasecmp(argv[1], "off") == 0) 1113 ftp_debug = 0; 1114 else { 1115 int val; 1116 1117 val = strsuftoi(argv[1]); 1118 if (val < 0) { 1119 fprintf(ttyout, "%s: bad debugging value.\n", 1120 argv[1]); 1121 code = -1; 1122 return; 1123 } 1124 ftp_debug = val; 1125 } 1126 } else 1127 ftp_debug = !ftp_debug; 1128 if (ftp_debug) 1129 options |= SO_DEBUG; 1130 else 1131 options &= ~SO_DEBUG; 1132 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(ftp_debug), ftp_debug); 1133 code = ftp_debug > 0; 1134 } 1135 1136 /* 1137 * Set current working directory on remote machine. 1138 */ 1139 void 1140 cd(int argc, char *argv[]) 1141 { 1142 int r; 1143 1144 if (argc == 0 || argc > 2 || 1145 (argc == 1 && !another(&argc, &argv, "remote-directory"))) { 1146 UPRINTF("usage: %s remote-directory\n", argv[0]); 1147 code = -1; 1148 return; 1149 } 1150 r = command("CWD %s", argv[1]); 1151 if (r == ERROR && code == 500) { 1152 if (verbose) 1153 fputs("CWD command not recognized, trying XCWD.\n", 1154 ttyout); 1155 r = command("XCWD %s", argv[1]); 1156 } 1157 if (r == COMPLETE) { 1158 dirchange = 1; 1159 remotecwd[0] = '\0'; 1160 remcwdvalid = 0; 1161 } 1162 } 1163 1164 /* 1165 * Set current working directory on local machine. 1166 */ 1167 void 1168 lcd(int argc, char *argv[]) 1169 { 1170 char *locdir; 1171 1172 code = -1; 1173 if (argc == 1) { 1174 argc++; 1175 argv[1] = localhome; 1176 } 1177 if (argc != 2) { 1178 UPRINTF("usage: %s [local-directory]\n", argv[0]); 1179 return; 1180 } 1181 if ((locdir = globulize(argv[1])) == NULL) 1182 return; 1183 if (chdir(locdir) == -1) 1184 warn("Can't chdir `%s'", locdir); 1185 else { 1186 updatelocalcwd(); 1187 if (localcwd[0]) { 1188 fprintf(ttyout, "Local directory now: %s\n", localcwd); 1189 code = 0; 1190 } else { 1191 fprintf(ttyout, "Unable to determine local directory\n"); 1192 } 1193 } 1194 (void)free(locdir); 1195 } 1196 1197 /* 1198 * Delete a single file. 1199 */ 1200 void 1201 delete(int argc, char *argv[]) 1202 { 1203 1204 if (argc == 0 || argc > 2 || 1205 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 1206 UPRINTF("usage: %s remote-file\n", argv[0]); 1207 code = -1; 1208 return; 1209 } 1210 if (command("DELE %s", argv[1]) == COMPLETE) 1211 dirchange = 1; 1212 } 1213 1214 /* 1215 * Delete multiple files. 1216 */ 1217 void 1218 mdelete(int argc, char *argv[]) 1219 { 1220 sigfunc oldintr; 1221 int ointer; 1222 char *cp; 1223 1224 if (argc == 0 || 1225 (argc == 1 && !another(&argc, &argv, "remote-files"))) { 1226 UPRINTF("usage: %s [remote-files]\n", argv[0]); 1227 code = -1; 1228 return; 1229 } 1230 mflag = 1; 1231 oldintr = xsignal(SIGINT, mintr); 1232 if (sigsetjmp(jabort, 1)) 1233 mabort(argv[0]); 1234 while ((cp = remglob(argv, 0, NULL)) != NULL) { 1235 if (*cp == '\0') { 1236 mflag = 0; 1237 continue; 1238 } 1239 if (mflag && confirm(argv[0], cp)) { 1240 if (command("DELE %s", cp) == COMPLETE) 1241 dirchange = 1; 1242 if (!mflag && fromatty) { 1243 ointer = interactive; 1244 interactive = 1; 1245 if (confirm(argv[0], NULL)) { 1246 mflag++; 1247 } 1248 interactive = ointer; 1249 } 1250 } 1251 } 1252 (void)xsignal(SIGINT, oldintr); 1253 mflag = 0; 1254 } 1255 1256 /* 1257 * Rename a remote file. 1258 */ 1259 void 1260 renamefile(int argc, char *argv[]) 1261 { 1262 1263 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name"))) 1264 goto usage; 1265 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { 1266 usage: 1267 UPRINTF("usage: %s from-name to-name\n", argv[0]); 1268 code = -1; 1269 return; 1270 } 1271 if (command("RNFR %s", argv[1]) == CONTINUE && 1272 command("RNTO %s", argv[2]) == COMPLETE) 1273 dirchange = 1; 1274 } 1275 1276 /* 1277 * Get a directory listing of remote files. 1278 * Supports being invoked as: 1279 * cmd runs 1280 * --- ---- 1281 * dir, ls LIST 1282 * mlsd MLSD 1283 * nlist NLST 1284 * pdir, pls LIST |$PAGER 1285 * pmlsd MLSD |$PAGER 1286 */ 1287 void 1288 ls(int argc, char *argv[]) 1289 { 1290 const char *cmd; 1291 char *remdir, *locbuf, *locfile; 1292 int pagecmd, mlsdcmd; 1293 1294 remdir = NULL; 1295 locbuf = NULL; 1296 pagecmd = mlsdcmd = 0; 1297 /* 1298 * the only commands that start with `p' are 1299 * the `pager' versions. 1300 */ 1301 if (argv[0][0] == 'p') 1302 pagecmd = 1; 1303 if (strcmp(argv[0] + pagecmd , "mlsd") == 0) { 1304 if (! features[FEAT_MLST]) { 1305 fprintf(ttyout, 1306 "MLSD is not supported by the remote server.\n"); 1307 return; 1308 } 1309 mlsdcmd = 1; 1310 } 1311 if (argc == 0) 1312 goto usage; 1313 1314 if (mlsdcmd) 1315 cmd = "MLSD"; 1316 else if (strcmp(argv[0] + pagecmd, "nlist") == 0) 1317 cmd = "NLST"; 1318 else 1319 cmd = "LIST"; 1320 1321 if (argc > 1) 1322 remdir = argv[1]; 1323 if (argc > 2) 1324 locfile = argv[2]; 1325 else 1326 locfile = locbuf = ftp_strdup("-"); 1327 if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) { 1328 usage: 1329 if (pagecmd || mlsdcmd) 1330 UPRINTF("usage: %s [remote-path]\n", argv[0]); 1331 else 1332 UPRINTF("usage: %s [remote-path [local-file]]\n", 1333 argv[0]); 1334 code = -1; 1335 goto freels; 1336 } 1337 1338 if (pagecmd) { 1339 const char *p; 1340 size_t len; 1341 1342 p = getoptionvalue("pager"); 1343 if (EMPTYSTRING(p)) 1344 p = DEFAULTPAGER; 1345 len = strlen(p) + 2; 1346 free(locbuf); 1347 locbuf = ftp_malloc(len); 1348 locbuf[0] = '|'; 1349 (void)strlcpy(locbuf + 1, p, len - 1); 1350 locfile = locbuf; 1351 } else if (locfile != locbuf) { 1352 if ((locbuf = globulize(locfile)) == NULL || 1353 !confirm("output to local-file:", locbuf)) { 1354 code = -1; 1355 goto freels; 1356 } 1357 locfile = locbuf; 1358 } 1359 recvrequest(cmd, locfile, remdir, "w", 0, 0); 1360 freels: 1361 if (locbuf) 1362 (void)free(locbuf); 1363 } 1364 1365 /* 1366 * Get a directory listing of multiple remote files. 1367 */ 1368 void 1369 mls(int argc, char *argv[]) 1370 { 1371 sigfunc oldintr; 1372 int ointer, i; 1373 int volatile dolist; 1374 char * volatile dest, *odest; 1375 const char *lmode; 1376 1377 if (argc == 0) 1378 goto usage; 1379 if (argc < 2 && !another(&argc, &argv, "remote-files")) 1380 goto usage; 1381 if (argc < 3 && !another(&argc, &argv, "local-file")) { 1382 usage: 1383 UPRINTF("usage: %s remote-files local-file\n", argv[0]); 1384 code = -1; 1385 return; 1386 } 1387 odest = dest = argv[argc - 1]; 1388 argv[argc - 1] = NULL; 1389 if (strcmp(dest, "-") && *dest != '|') 1390 if (((dest = globulize(dest)) == NULL) || 1391 !confirm("output to local-file:", dest)) { 1392 code = -1; 1393 return; 1394 } 1395 dolist = strcmp(argv[0], "mls"); 1396 mflag = 1; 1397 oldintr = xsignal(SIGINT, mintr); 1398 if (sigsetjmp(jabort, 1)) 1399 mabort(argv[0]); 1400 for (i = 1; mflag && i < argc-1 && connected; i++) { 1401 lmode = (i == 1) ? "w" : "a"; 1402 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], lmode, 1403 0, 0); 1404 if (!mflag && fromatty) { 1405 ointer = interactive; 1406 interactive = 1; 1407 if (confirm(argv[0], NULL)) { 1408 mflag++; 1409 } 1410 interactive = ointer; 1411 } 1412 } 1413 (void)xsignal(SIGINT, oldintr); 1414 mflag = 0; 1415 if (dest != odest) /* free up after globulize() */ 1416 free(dest); 1417 } 1418 1419 /* 1420 * Do a shell escape 1421 */ 1422 /*ARGSUSED*/ 1423 void 1424 shell(int argc, char *argv[]) 1425 { 1426 pid_t pid; 1427 sigfunc oldintr; 1428 char shellnam[MAXPATHLEN]; 1429 const char *shellp, *namep; 1430 int wait_status; 1431 1432 if (argc == 0) { 1433 UPRINTF("usage: %s [command [args]]\n", argv[0]); 1434 code = -1; 1435 return; 1436 } 1437 oldintr = xsignal(SIGINT, SIG_IGN); 1438 if ((pid = fork()) == 0) { 1439 for (pid = 3; pid < 20; pid++) 1440 (void)close(pid); 1441 (void)xsignal(SIGINT, SIG_DFL); 1442 shellp = getenv("SHELL"); 1443 if (shellp == NULL) 1444 shellp = _PATH_BSHELL; 1445 namep = strrchr(shellp, '/'); 1446 if (namep == NULL) 1447 namep = shellp; 1448 else 1449 namep++; 1450 (void)strlcpy(shellnam, namep, sizeof(shellnam)); 1451 if (ftp_debug) { 1452 fputs(shellp, ttyout); 1453 putc('\n', ttyout); 1454 } 1455 if (argc > 1) { 1456 execl(shellp, shellnam, "-c", altarg, (char *)0); 1457 } 1458 else { 1459 execl(shellp, shellnam, (char *)0); 1460 } 1461 warn("Can't execute `%s'", shellp); 1462 code = -1; 1463 exit(1); 1464 } 1465 if (pid > 0) 1466 while (wait(&wait_status) != pid) 1467 ; 1468 (void)xsignal(SIGINT, oldintr); 1469 if (pid == -1) { 1470 warn("Can't fork a subshell; try again later"); 1471 code = -1; 1472 } else 1473 code = 0; 1474 } 1475 1476 /* 1477 * Send new user information (re-login) 1478 */ 1479 void 1480 user(int argc, char *argv[]) 1481 { 1482 char *password; 1483 char emptypass[] = ""; 1484 int n, aflag = 0; 1485 1486 if (argc == 0) 1487 goto usage; 1488 if (argc < 2) 1489 (void)another(&argc, &argv, "username"); 1490 if (argc < 2 || argc > 4) { 1491 usage: 1492 UPRINTF("usage: %s username [password [account]]\n", 1493 argv[0]); 1494 code = -1; 1495 return; 1496 } 1497 n = command("USER %s", argv[1]); 1498 if (n == CONTINUE) { 1499 if (argc < 3) { 1500 password = getpass("Password: "); 1501 if (password == NULL) 1502 password = emptypass; 1503 } else { 1504 password = argv[2]; 1505 } 1506 n = command("PASS %s", password); 1507 memset(password, 0, strlen(password)); 1508 } 1509 if (n == CONTINUE) { 1510 aflag++; 1511 if (argc < 4) { 1512 password = getpass("Account: "); 1513 if (password == NULL) 1514 password = emptypass; 1515 } else { 1516 password = argv[3]; 1517 } 1518 n = command("ACCT %s", password); 1519 memset(password, 0, strlen(password)); 1520 } 1521 if (n != COMPLETE) { 1522 fputs("Login failed.\n", ttyout); 1523 return; 1524 } 1525 if (!aflag && argc == 4) { 1526 password = argv[3]; 1527 (void)command("ACCT %s", password); 1528 memset(password, 0, strlen(password)); 1529 } 1530 connected = -1; 1531 getremoteinfo(); 1532 } 1533 1534 /* 1535 * Print working directory on remote machine. 1536 */ 1537 /*VARARGS*/ 1538 void 1539 pwd(int argc, char *argv[]) 1540 { 1541 1542 code = -1; 1543 if (argc != 1) { 1544 UPRINTF("usage: %s\n", argv[0]); 1545 return; 1546 } 1547 if (!remcwdvalid || remotecwd[0] == '\0') 1548 updateremotecwd(); 1549 if (remotecwd[0] == '\0') 1550 fprintf(ttyout, "Unable to determine remote directory\n"); 1551 else { 1552 fprintf(ttyout, "Remote directory: %s\n", remotecwd); 1553 code = 0; 1554 } 1555 } 1556 1557 /* 1558 * Print working directory on local machine. 1559 */ 1560 void 1561 lpwd(int argc, char *argv[]) 1562 { 1563 1564 code = -1; 1565 if (argc != 1) { 1566 UPRINTF("usage: %s\n", argv[0]); 1567 return; 1568 } 1569 if (! localcwd[0]) 1570 updatelocalcwd(); 1571 if (! localcwd[0]) 1572 fprintf(ttyout, "Unable to determine local directory\n"); 1573 else { 1574 fprintf(ttyout, "Local directory: %s\n", localcwd); 1575 code = 0; 1576 } 1577 } 1578 1579 /* 1580 * Make a directory. 1581 */ 1582 void 1583 makedir(int argc, char *argv[]) 1584 { 1585 int r; 1586 1587 if (argc == 0 || argc > 2 || 1588 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1589 UPRINTF("usage: %s directory-name\n", argv[0]); 1590 code = -1; 1591 return; 1592 } 1593 r = command("MKD %s", argv[1]); 1594 if (r == ERROR && code == 500) { 1595 if (verbose) 1596 fputs("MKD command not recognized, trying XMKD.\n", 1597 ttyout); 1598 r = command("XMKD %s", argv[1]); 1599 } 1600 if (r == COMPLETE) 1601 dirchange = 1; 1602 } 1603 1604 /* 1605 * Remove a directory. 1606 */ 1607 void 1608 removedir(int argc, char *argv[]) 1609 { 1610 int r; 1611 1612 if (argc == 0 || argc > 2 || 1613 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1614 UPRINTF("usage: %s directory-name\n", argv[0]); 1615 code = -1; 1616 return; 1617 } 1618 r = command("RMD %s", argv[1]); 1619 if (r == ERROR && code == 500) { 1620 if (verbose) 1621 fputs("RMD command not recognized, trying XRMD.\n", 1622 ttyout); 1623 r = command("XRMD %s", argv[1]); 1624 } 1625 if (r == COMPLETE) 1626 dirchange = 1; 1627 } 1628 1629 /* 1630 * Send a line, verbatim, to the remote machine. 1631 */ 1632 void 1633 quote(int argc, char *argv[]) 1634 { 1635 1636 if (argc == 0 || 1637 (argc == 1 && !another(&argc, &argv, "command line to send"))) { 1638 UPRINTF("usage: %s line-to-send\n", argv[0]); 1639 code = -1; 1640 return; 1641 } 1642 quote1("", argc, argv); 1643 } 1644 1645 /* 1646 * Send a SITE command to the remote machine. The line 1647 * is sent verbatim to the remote machine, except that the 1648 * word "SITE" is added at the front. 1649 */ 1650 void 1651 site(int argc, char *argv[]) 1652 { 1653 1654 if (argc == 0 || 1655 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){ 1656 UPRINTF("usage: %s line-to-send\n", argv[0]); 1657 code = -1; 1658 return; 1659 } 1660 quote1("SITE ", argc, argv); 1661 } 1662 1663 /* 1664 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1665 * Send the result as a one-line command and get response. 1666 */ 1667 void 1668 quote1(const char *initial, int argc, char *argv[]) 1669 { 1670 int i; 1671 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1672 1673 (void)strlcpy(buf, initial, sizeof(buf)); 1674 for (i = 1; i < argc; i++) { 1675 (void)strlcat(buf, argv[i], sizeof(buf)); 1676 if (i < (argc - 1)) 1677 (void)strlcat(buf, " ", sizeof(buf)); 1678 } 1679 if (command("%s", buf) == PRELIM) { 1680 while (getreply(0) == PRELIM) 1681 continue; 1682 } 1683 dirchange = 1; 1684 } 1685 1686 void 1687 do_chmod(int argc, char *argv[]) 1688 { 1689 1690 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode"))) 1691 goto usage; 1692 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 1693 usage: 1694 UPRINTF("usage: %s mode remote-file\n", argv[0]); 1695 code = -1; 1696 return; 1697 } 1698 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1699 } 1700 1701 #define COMMAND_1ARG(argc, argv, cmd) \ 1702 if (argc == 1) \ 1703 command(cmd); \ 1704 else \ 1705 command(cmd " %s", argv[1]) 1706 1707 void 1708 do_umask(int argc, char *argv[]) 1709 { 1710 int oldverbose = verbose; 1711 1712 if (argc == 0) { 1713 UPRINTF("usage: %s [umask]\n", argv[0]); 1714 code = -1; 1715 return; 1716 } 1717 verbose = 1; 1718 COMMAND_1ARG(argc, argv, "SITE UMASK"); 1719 verbose = oldverbose; 1720 } 1721 1722 void 1723 idlecmd(int argc, char *argv[]) 1724 { 1725 int oldverbose = verbose; 1726 1727 if (argc < 1 || argc > 2) { 1728 UPRINTF("usage: %s [seconds]\n", argv[0]); 1729 code = -1; 1730 return; 1731 } 1732 verbose = 1; 1733 COMMAND_1ARG(argc, argv, "SITE IDLE"); 1734 verbose = oldverbose; 1735 } 1736 1737 /* 1738 * Ask the other side for help. 1739 */ 1740 void 1741 rmthelp(int argc, char *argv[]) 1742 { 1743 int oldverbose = verbose; 1744 1745 if (argc == 0) { 1746 UPRINTF("usage: %s\n", argv[0]); 1747 code = -1; 1748 return; 1749 } 1750 verbose = 1; 1751 COMMAND_1ARG(argc, argv, "HELP"); 1752 verbose = oldverbose; 1753 } 1754 1755 /* 1756 * Terminate session and exit. 1757 * May be called with 0, NULL. 1758 */ 1759 /*VARARGS*/ 1760 void 1761 quit(int argc, char *argv[]) 1762 { 1763 1764 /* this may be called with argc == 0, argv == NULL */ 1765 if (argc == 0 && argv != NULL) { 1766 UPRINTF("usage: %s\n", argv[0]); 1767 code = -1; 1768 return; 1769 } 1770 if (connected) 1771 disconnect(0, NULL); 1772 pswitch(1); 1773 if (connected) 1774 disconnect(0, NULL); 1775 exit(0); 1776 } 1777 1778 void __dead 1779 justquit(void) 1780 { 1781 1782 quit(0, NULL); 1783 /* 1784 * quit is not __dead, but for our invocation it never will return, 1785 * but some compilers are not smart enough to find this out. 1786 */ 1787 exit(0); 1788 } 1789 1790 /* 1791 * Terminate session, but don't exit. 1792 * May be called with 0, NULL. 1793 */ 1794 void 1795 disconnect(int argc, char *argv[]) 1796 { 1797 1798 /* this may be called with argc == 0, argv == NULL */ 1799 if (argc == 0 && argv != NULL) { 1800 UPRINTF("usage: %s\n", argv[0]); 1801 code = -1; 1802 return; 1803 } 1804 if (!connected) 1805 return; 1806 (void)command("QUIT"); 1807 cleanuppeer(); 1808 } 1809 1810 void 1811 account(int argc, char *argv[]) 1812 { 1813 char *ap; 1814 char emptypass[] = ""; 1815 1816 if (argc == 0 || argc > 2) { 1817 UPRINTF("usage: %s [password]\n", argv[0]); 1818 code = -1; 1819 return; 1820 } 1821 else if (argc == 2) 1822 ap = argv[1]; 1823 else { 1824 ap = getpass("Account:"); 1825 if (ap == NULL) 1826 ap = emptypass; 1827 } 1828 (void)command("ACCT %s", ap); 1829 memset(ap, 0, strlen(ap)); 1830 } 1831 1832 static sigjmp_buf abortprox; 1833 1834 void 1835 proxabort(int notused __unused) 1836 { 1837 1838 sigint_raised = 1; 1839 alarmtimer(0); 1840 if (!proxy) { 1841 pswitch(1); 1842 } 1843 if (connected) { 1844 proxflag = 1; 1845 } 1846 else { 1847 proxflag = 0; 1848 } 1849 pswitch(0); 1850 siglongjmp(abortprox, 1); 1851 } 1852 1853 void 1854 doproxy(int argc, char *argv[]) 1855 { 1856 struct cmd *c; 1857 size_t cmdpos; 1858 sigfunc oldintr; 1859 char cmdbuf[MAX_C_NAME]; 1860 1861 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { 1862 UPRINTF("usage: %s command\n", argv[0]); 1863 code = -1; 1864 return; 1865 } 1866 c = getcmd(argv[1]); 1867 if (c == (struct cmd *) -1) { 1868 fputs("?Ambiguous command.\n", ttyout); 1869 code = -1; 1870 return; 1871 } 1872 if (c == 0) { 1873 fputs("?Invalid command.\n", ttyout); 1874 code = -1; 1875 return; 1876 } 1877 if (!c->c_proxy) { 1878 fputs("?Invalid proxy command.\n", ttyout); 1879 code = -1; 1880 return; 1881 } 1882 if (sigsetjmp(abortprox, 1)) { 1883 code = -1; 1884 return; 1885 } 1886 oldintr = xsignal(SIGINT, proxabort); 1887 pswitch(1); 1888 if (c->c_conn && !connected) { 1889 fputs("Not connected.\n", ttyout); 1890 pswitch(0); 1891 (void)xsignal(SIGINT, oldintr); 1892 code = -1; 1893 return; 1894 } 1895 cmdpos = strcspn(line, " \t"); 1896 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1897 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1898 (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); 1899 argv[1] = cmdbuf; 1900 (*c->c_handler)(argc-1, argv+1); 1901 if (connected) { 1902 proxflag = 1; 1903 } 1904 else { 1905 proxflag = 0; 1906 } 1907 pswitch(0); 1908 (void)xsignal(SIGINT, oldintr); 1909 } 1910 1911 void 1912 setcase(int argc, char *argv[]) 1913 { 1914 1915 code = togglevar(argc, argv, &mcase, "Case mapping"); 1916 } 1917 1918 /* 1919 * convert the given name to lower case if it's all upper case, into 1920 * a static buffer which is returned to the caller 1921 */ 1922 static char * 1923 docase(char *dst, size_t dlen, const char *src) 1924 { 1925 size_t i; 1926 int dochange = 1; 1927 1928 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) { 1929 dst[i] = src[i]; 1930 if (islower((unsigned char)dst[i])) 1931 dochange = 0; 1932 } 1933 dst[i] = '\0'; 1934 1935 if (dochange) { 1936 for (i = 0; dst[i] != '\0'; i++) 1937 if (isupper((unsigned char)dst[i])) 1938 dst[i] = tolower((unsigned char)dst[i]); 1939 } 1940 return dst; 1941 } 1942 1943 void 1944 setcr(int argc, char *argv[]) 1945 { 1946 1947 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1948 } 1949 1950 void 1951 setntrans(int argc, char *argv[]) 1952 { 1953 1954 if (argc == 0 || argc > 3) { 1955 UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]); 1956 code = -1; 1957 return; 1958 } 1959 if (argc == 1) { 1960 ntflag = 0; 1961 fputs("Ntrans off.\n", ttyout); 1962 code = ntflag; 1963 return; 1964 } 1965 ntflag++; 1966 code = ntflag; 1967 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1968 if (argc == 2) { 1969 ntout[0] = '\0'; 1970 return; 1971 } 1972 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1973 } 1974 1975 #define ADDC(x) \ 1976 do { \ 1977 *cp2++ = x; \ 1978 if (cp2 - dst >= (ptrdiff_t)(dlen - 1)) \ 1979 goto out; \ 1980 } while (0) 1981 1982 static char * 1983 dotrans(char *dst, size_t dlen, const char *src) 1984 { 1985 const char *cp1; 1986 char *cp2 = dst; 1987 size_t i, ostop; 1988 1989 for (ostop = 0; ntout[ostop] && ostop < sizeof(ntout); ostop++) 1990 continue; 1991 for (cp1 = src; *cp1; cp1++) { 1992 int found = 0; 1993 for (i = 0; i < sizeof(ntin) && ntin[i]; i++) { 1994 if (*cp1 == ntin[i]) { 1995 found++; 1996 if (i < ostop) { 1997 ADDC(ntout[i]); 1998 } 1999 break; 2000 } 2001 } 2002 if (!found) { 2003 ADDC(*cp1); 2004 } 2005 } 2006 out: 2007 *cp2 = '\0'; 2008 return dst; 2009 } 2010 2011 void 2012 setnmap(int argc, char *argv[]) 2013 { 2014 char *cp; 2015 2016 if (argc == 1) { 2017 mapflag = 0; 2018 fputs("Nmap off.\n", ttyout); 2019 code = mapflag; 2020 return; 2021 } 2022 if (argc == 0 || 2023 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { 2024 UPRINTF("usage: %s [mapin mapout]\n", argv[0]); 2025 code = -1; 2026 return; 2027 } 2028 mapflag = 1; 2029 code = 1; 2030 cp = strchr(altarg, ' '); 2031 if (proxy) { 2032 while(*++cp == ' ') 2033 continue; 2034 altarg = cp; 2035 cp = strchr(altarg, ' '); 2036 } 2037 *cp = '\0'; 2038 (void)strlcpy(mapin, altarg, MAXPATHLEN); 2039 while (*++cp == ' ') 2040 continue; 2041 (void)strlcpy(mapout, cp, MAXPATHLEN); 2042 } 2043 2044 static char * 2045 domap(char *dst, size_t dlen, const char *src) 2046 { 2047 const char *cp1 = src; 2048 char *cp2 = mapin; 2049 const char *tp[9], *te[9]; 2050 int i, toks[9], toknum = 0, match = 1; 2051 2052 for (i=0; i < 9; ++i) { 2053 toks[i] = 0; 2054 } 2055 while (match && *cp1 && *cp2) { 2056 switch (*cp2) { 2057 case '\\': 2058 if (*++cp2 != *cp1) { 2059 match = 0; 2060 } 2061 break; 2062 case '$': 2063 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 2064 if (*cp1 != *(++cp2+1)) { 2065 toks[toknum = *cp2 - '1']++; 2066 tp[toknum] = cp1; 2067 while (*++cp1 && *(cp2+1) 2068 != *cp1); 2069 te[toknum] = cp1; 2070 } 2071 cp2++; 2072 break; 2073 } 2074 /* FALLTHROUGH */ 2075 default: 2076 if (*cp2 != *cp1) { 2077 match = 0; 2078 } 2079 break; 2080 } 2081 if (match && *cp1) { 2082 cp1++; 2083 } 2084 if (match && *cp2) { 2085 cp2++; 2086 } 2087 } 2088 if (!match && *cp1) /* last token mismatch */ 2089 { 2090 toks[toknum] = 0; 2091 } 2092 cp2 = dst; 2093 *cp2 = '\0'; 2094 cp1 = mapout; 2095 while (*cp1) { 2096 match = 0; 2097 switch (*cp1) { 2098 case '\\': 2099 if (*(cp1 + 1)) { 2100 ADDC(*++cp1); 2101 } 2102 break; 2103 case '[': 2104 LOOP: 2105 if (*++cp1 == '$' && 2106 isdigit((unsigned char)*(cp1+1))) { 2107 if (*++cp1 == '0') { 2108 const char *cp3 = src; 2109 2110 while (*cp3) { 2111 ADDC(*cp3++); 2112 } 2113 match = 1; 2114 } 2115 else if (toks[toknum = *cp1 - '1']) { 2116 const char *cp3 = tp[toknum]; 2117 2118 while (cp3 != te[toknum]) { 2119 ADDC(*cp3++); 2120 } 2121 match = 1; 2122 } 2123 } 2124 else { 2125 while (*cp1 && *cp1 != ',' && 2126 *cp1 != ']') { 2127 if (*cp1 == '\\') { 2128 cp1++; 2129 } 2130 else if (*cp1 == '$' && 2131 isdigit((unsigned char)*(cp1+1))) { 2132 if (*++cp1 == '0') { 2133 const char *cp3 = src; 2134 2135 while (*cp3) { 2136 ADDC(*cp3++); 2137 } 2138 } 2139 else if (toks[toknum = 2140 *cp1 - '1']) { 2141 const char *cp3=tp[toknum]; 2142 2143 while (cp3 != 2144 te[toknum]) { 2145 ADDC(*cp3++); 2146 } 2147 } 2148 } 2149 else if (*cp1) { 2150 ADDC(*cp1++); 2151 } 2152 } 2153 if (!*cp1) { 2154 goto unbalanced; 2155 } 2156 match = 1; 2157 cp1--; 2158 } 2159 if (match) { 2160 while (*++cp1 && *cp1 != ']') { 2161 if (*cp1 == '\\' && *(cp1 + 1)) { 2162 cp1++; 2163 } 2164 } 2165 if (!*cp1) { 2166 goto unbalanced; 2167 } 2168 break; 2169 } 2170 switch (*++cp1) { 2171 case ',': 2172 goto LOOP; 2173 case ']': 2174 break; 2175 default: 2176 cp1--; 2177 goto LOOP; 2178 } 2179 break; 2180 case '$': 2181 if (isdigit((unsigned char)*(cp1 + 1))) { 2182 if (*++cp1 == '0') { 2183 const char *cp3 = src; 2184 2185 while (*cp3) { 2186 ADDC(*cp3++); 2187 } 2188 } 2189 else if (toks[toknum = *cp1 - '1']) { 2190 const char *cp3 = tp[toknum]; 2191 2192 while (cp3 != te[toknum]) { 2193 ADDC(*cp3++); 2194 } 2195 } 2196 break; 2197 } 2198 /* FALLTHROUGH */ 2199 default: 2200 ADDC(*cp1); 2201 break; 2202 } 2203 cp1++; 2204 } 2205 out: 2206 *cp2 = '\0'; 2207 if (!*dst) 2208 strlcpy(dst, src, dlen); 2209 return dst; 2210 unbalanced: 2211 fputs("nmap: unbalanced brackets.\n", ttyout); 2212 *dst = '\0'; 2213 goto out; 2214 } 2215 2216 void 2217 setpassive(int argc, char *argv[]) 2218 { 2219 2220 if (argc == 1) { 2221 passivemode = !passivemode; 2222 activefallback = passivemode; 2223 } else if (argc != 2) { 2224 passiveusage: 2225 UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]); 2226 code = -1; 2227 return; 2228 } else if (strcasecmp(argv[1], "on") == 0) { 2229 passivemode = 1; 2230 activefallback = 0; 2231 } else if (strcasecmp(argv[1], "off") == 0) { 2232 passivemode = 0; 2233 activefallback = 0; 2234 } else if (strcasecmp(argv[1], "auto") == 0) { 2235 passivemode = 1; 2236 activefallback = 1; 2237 } else 2238 goto passiveusage; 2239 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 2240 onoff(passivemode), onoff(activefallback)); 2241 code = passivemode; 2242 } 2243 2244 2245 void 2246 setepsv4(int argc, char *argv[]) 2247 { 2248 code = togglevar(argc, argv, &epsv4, 2249 verbose ? "EPSV/EPRT on IPv4" : NULL); 2250 epsv4bad = 0; 2251 } 2252 2253 void 2254 setepsv6(int argc, char *argv[]) 2255 { 2256 code = togglevar(argc, argv, &epsv6, 2257 verbose ? "EPSV/EPRT on IPv6" : NULL); 2258 epsv6bad = 0; 2259 } 2260 2261 void 2262 setepsv(int argc, char*argv[]) 2263 { 2264 setepsv4(argc,argv); 2265 setepsv6(argc,argv); 2266 } 2267 2268 void 2269 setsunique(int argc, char *argv[]) 2270 { 2271 2272 code = togglevar(argc, argv, &sunique, "Store unique"); 2273 } 2274 2275 void 2276 setrunique(int argc, char *argv[]) 2277 { 2278 2279 code = togglevar(argc, argv, &runique, "Receive unique"); 2280 } 2281 2282 int 2283 parserate(int argc, char *argv[], int cmdlineopt) 2284 { 2285 int dir, max, incr, showonly; 2286 sigfunc oldusr1, oldusr2; 2287 2288 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { 2289 usage: 2290 if (cmdlineopt) 2291 UPRINTF( 2292 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", 2293 argv[0]); 2294 else 2295 UPRINTF( 2296 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", 2297 argv[0]); 2298 return -1; 2299 } 2300 dir = max = incr = showonly = 0; 2301 #define RATE_GET 1 2302 #define RATE_PUT 2 2303 #define RATE_ALL (RATE_GET | RATE_PUT) 2304 2305 if (strcasecmp(argv[1], "all") == 0) 2306 dir = RATE_ALL; 2307 else if (strcasecmp(argv[1], "get") == 0) 2308 dir = RATE_GET; 2309 else if (strcasecmp(argv[1], "put") == 0) 2310 dir = RATE_PUT; 2311 else 2312 goto usage; 2313 2314 if (argc >= 3) { 2315 if ((max = strsuftoi(argv[2])) < 0) 2316 goto usage; 2317 } else 2318 showonly = 1; 2319 2320 if (argc == 4) { 2321 if ((incr = strsuftoi(argv[3])) <= 0) 2322 goto usage; 2323 } else 2324 incr = DEFAULTINCR; 2325 2326 oldusr1 = xsignal(SIGUSR1, SIG_IGN); 2327 oldusr2 = xsignal(SIGUSR2, SIG_IGN); 2328 if (dir & RATE_GET) { 2329 if (!showonly) { 2330 rate_get = max; 2331 rate_get_incr = incr; 2332 } 2333 if (!cmdlineopt || verbose) 2334 fprintf(ttyout, 2335 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 2336 onoff(rate_get), rate_get, rate_get_incr); 2337 } 2338 if (dir & RATE_PUT) { 2339 if (!showonly) { 2340 rate_put = max; 2341 rate_put_incr = incr; 2342 } 2343 if (!cmdlineopt || verbose) 2344 fprintf(ttyout, 2345 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 2346 onoff(rate_put), rate_put, rate_put_incr); 2347 } 2348 (void)xsignal(SIGUSR1, oldusr1); 2349 (void)xsignal(SIGUSR2, oldusr2); 2350 return 0; 2351 } 2352 2353 void 2354 setrate(int argc, char *argv[]) 2355 { 2356 2357 code = parserate(argc, argv, 0); 2358 } 2359 2360 /* change directory to parent directory */ 2361 void 2362 cdup(int argc, char *argv[]) 2363 { 2364 int r; 2365 2366 if (argc == 0) { 2367 UPRINTF("usage: %s\n", argv[0]); 2368 code = -1; 2369 return; 2370 } 2371 r = command("CDUP"); 2372 if (r == ERROR && code == 500) { 2373 if (verbose) 2374 fputs("CDUP command not recognized, trying XCUP.\n", 2375 ttyout); 2376 r = command("XCUP"); 2377 } 2378 if (r == COMPLETE) { 2379 dirchange = 1; 2380 remotecwd[0] = '\0'; 2381 remcwdvalid = 0; 2382 } 2383 } 2384 2385 /* 2386 * Restart transfer at specific point 2387 */ 2388 void 2389 restart(int argc, char *argv[]) 2390 { 2391 2392 if (argc == 0 || argc > 2) { 2393 UPRINTF("usage: %s [restart-point]\n", argv[0]); 2394 code = -1; 2395 return; 2396 } 2397 if (! features[FEAT_REST_STREAM]) { 2398 fprintf(ttyout, 2399 "Restart is not supported by the remote server.\n"); 2400 return; 2401 } 2402 if (argc == 2) { 2403 off_t rp; 2404 char *ep; 2405 2406 rp = STRTOLL(argv[1], &ep, 10); 2407 if (rp < 0 || *ep != '\0') 2408 fprintf(ttyout, "restart: Invalid offset `%s'\n", 2409 argv[1]); 2410 else 2411 restart_point = rp; 2412 } 2413 if (restart_point == 0) 2414 fputs("No restart point defined.\n", ttyout); 2415 else 2416 fprintf(ttyout, 2417 "Restarting at " LLF " for next get, put or append\n", 2418 (LLT)restart_point); 2419 } 2420 2421 /* 2422 * Show remote system type 2423 */ 2424 void 2425 syst(int argc, char *argv[]) 2426 { 2427 int oldverbose = verbose; 2428 2429 if (argc == 0) { 2430 UPRINTF("usage: %s\n", argv[0]); 2431 code = -1; 2432 return; 2433 } 2434 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2435 (void)command("SYST"); 2436 verbose = oldverbose; 2437 } 2438 2439 void 2440 macdef(int argc, char *argv[]) 2441 { 2442 char *tmp; 2443 int c; 2444 2445 if (argc == 0) 2446 goto usage; 2447 if (macnum == 16) { 2448 fputs("Limit of 16 macros have already been defined.\n", 2449 ttyout); 2450 code = -1; 2451 return; 2452 } 2453 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { 2454 usage: 2455 UPRINTF("usage: %s macro_name\n", argv[0]); 2456 code = -1; 2457 return; 2458 } 2459 if (interactive) 2460 fputs( 2461 "Enter macro line by line, terminating it with a null line.\n", 2462 ttyout); 2463 (void)strlcpy(macros[macnum].mac_name, argv[1], 2464 sizeof(macros[macnum].mac_name)); 2465 if (macnum == 0) 2466 macros[macnum].mac_start = macbuf; 2467 else 2468 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2469 tmp = macros[macnum].mac_start; 2470 while (tmp != macbuf+4096) { 2471 if ((c = getchar()) == EOF) { 2472 fputs("macdef: end of file encountered.\n", ttyout); 2473 code = -1; 2474 return; 2475 } 2476 if ((*tmp = c) == '\n') { 2477 if (tmp == macros[macnum].mac_start) { 2478 macros[macnum++].mac_end = tmp; 2479 code = 0; 2480 return; 2481 } 2482 if (*(tmp-1) == '\0') { 2483 macros[macnum++].mac_end = tmp - 1; 2484 code = 0; 2485 return; 2486 } 2487 *tmp = '\0'; 2488 } 2489 tmp++; 2490 } 2491 for (;;) { 2492 while ((c = getchar()) != '\n' && c != EOF) 2493 /* LOOP */; 2494 if (c == EOF || getchar() == '\n') { 2495 fputs("Macro not defined - 4 KiB buffer exceeded.\n", 2496 ttyout); 2497 code = -1; 2498 return; 2499 } 2500 } 2501 } 2502 2503 /* 2504 * Get size of file on remote machine 2505 */ 2506 void 2507 sizecmd(int argc, char *argv[]) 2508 { 2509 off_t size; 2510 2511 if (argc == 0 || argc > 2 || 2512 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2513 UPRINTF("usage: %s remote-file\n", argv[0]); 2514 code = -1; 2515 return; 2516 } 2517 size = remotesize(argv[1], 1); 2518 if (size != -1) 2519 fprintf(ttyout, 2520 "%s\t" LLF "\n", argv[1], (LLT)size); 2521 code = (size > 0); 2522 } 2523 2524 /* 2525 * Get last modification time of file on remote machine 2526 */ 2527 void 2528 modtime(int argc, char *argv[]) 2529 { 2530 time_t mtime; 2531 2532 if (argc == 0 || argc > 2 || 2533 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2534 UPRINTF("usage: %s remote-file\n", argv[0]); 2535 code = -1; 2536 return; 2537 } 2538 mtime = remotemodtime(argv[1], 1); 2539 if (mtime != -1) 2540 fprintf(ttyout, "%s\t%s", argv[1], 2541 rfc2822time(localtime(&mtime))); 2542 code = (mtime > 0); 2543 } 2544 2545 /* 2546 * Show status on remote machine 2547 */ 2548 void 2549 rmtstatus(int argc, char *argv[]) 2550 { 2551 2552 if (argc == 0) { 2553 UPRINTF("usage: %s [remote-file]\n", argv[0]); 2554 code = -1; 2555 return; 2556 } 2557 COMMAND_1ARG(argc, argv, "STAT"); 2558 } 2559 2560 /* 2561 * Get file if modtime is more recent than current file 2562 */ 2563 void 2564 newer(int argc, char *argv[]) 2565 { 2566 2567 if (getit(argc, argv, -1, "w")) 2568 fprintf(ttyout, 2569 "Local file \"%s\" is newer than remote file \"%s\".\n", 2570 argv[2], argv[1]); 2571 } 2572 2573 /* 2574 * Display one local file through $PAGER. 2575 */ 2576 void 2577 lpage(int argc, char *argv[]) 2578 { 2579 size_t len; 2580 const char *p; 2581 char *pager, *locfile; 2582 2583 if (argc == 0 || argc > 2 || 2584 (argc == 1 && !another(&argc, &argv, "local-file"))) { 2585 UPRINTF("usage: %s local-file\n", argv[0]); 2586 code = -1; 2587 return; 2588 } 2589 if ((locfile = globulize(argv[1])) == NULL) { 2590 code = -1; 2591 return; 2592 } 2593 p = getoptionvalue("pager"); 2594 if (EMPTYSTRING(p)) 2595 p = DEFAULTPAGER; 2596 len = strlen(p) + strlen(locfile) + 2; 2597 pager = ftp_malloc(len); 2598 (void)strlcpy(pager, p, len); 2599 (void)strlcat(pager, " ", len); 2600 (void)strlcat(pager, locfile, len); 2601 system(pager); 2602 code = 0; 2603 (void)free(pager); 2604 (void)free(locfile); 2605 } 2606 2607 /* 2608 * Display one remote file through $PAGER. 2609 */ 2610 void 2611 page(int argc, char *argv[]) 2612 { 2613 int ohash, overbose; 2614 off_t orestart_point; 2615 size_t len; 2616 const char *p; 2617 char *pager; 2618 2619 if (argc == 0 || argc > 2 || 2620 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2621 UPRINTF("usage: %s remote-file\n", argv[0]); 2622 code = -1; 2623 return; 2624 } 2625 p = getoptionvalue("pager"); 2626 if (EMPTYSTRING(p)) 2627 p = DEFAULTPAGER; 2628 len = strlen(p) + 2; 2629 pager = ftp_malloc(len); 2630 pager[0] = '|'; 2631 (void)strlcpy(pager + 1, p, len - 1); 2632 2633 ohash = hash; 2634 orestart_point = restart_point; 2635 overbose = verbose; 2636 hash = verbose = 0; 2637 restart_point = 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