1 /* $NetBSD: cmds.c,v 1.138 2017/11/20 21:11:36 kre Exp $ */ 2 3 /*- 4 * Copyright (c) 1996-2009 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.138 2017/11/20 21:11:36 kre 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 (ftp_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 /* 1780 * Terminate session, but don't exit. 1781 * May be called with 0, NULL. 1782 */ 1783 void 1784 disconnect(int argc, char *argv[]) 1785 { 1786 1787 /* this may be called with argc == 0, argv == NULL */ 1788 if (argc == 0 && argv != NULL) { 1789 UPRINTF("usage: %s\n", argv[0]); 1790 code = -1; 1791 return; 1792 } 1793 if (!connected) 1794 return; 1795 (void)command("QUIT"); 1796 cleanuppeer(); 1797 } 1798 1799 void 1800 account(int argc, char *argv[]) 1801 { 1802 char *ap; 1803 char emptypass[] = ""; 1804 1805 if (argc == 0 || argc > 2) { 1806 UPRINTF("usage: %s [password]\n", argv[0]); 1807 code = -1; 1808 return; 1809 } 1810 else if (argc == 2) 1811 ap = argv[1]; 1812 else { 1813 ap = getpass("Account:"); 1814 if (ap == NULL) 1815 ap = emptypass; 1816 } 1817 (void)command("ACCT %s", ap); 1818 memset(ap, 0, strlen(ap)); 1819 } 1820 1821 sigjmp_buf abortprox; 1822 1823 void 1824 proxabort(int notused) 1825 { 1826 1827 sigint_raised = 1; 1828 alarmtimer(0); 1829 if (!proxy) { 1830 pswitch(1); 1831 } 1832 if (connected) { 1833 proxflag = 1; 1834 } 1835 else { 1836 proxflag = 0; 1837 } 1838 pswitch(0); 1839 siglongjmp(abortprox, 1); 1840 } 1841 1842 void 1843 doproxy(int argc, char *argv[]) 1844 { 1845 struct cmd *c; 1846 int cmdpos; 1847 sigfunc oldintr; 1848 char cmdbuf[MAX_C_NAME]; 1849 1850 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { 1851 UPRINTF("usage: %s command\n", argv[0]); 1852 code = -1; 1853 return; 1854 } 1855 c = getcmd(argv[1]); 1856 if (c == (struct cmd *) -1) { 1857 fputs("?Ambiguous command.\n", ttyout); 1858 code = -1; 1859 return; 1860 } 1861 if (c == 0) { 1862 fputs("?Invalid command.\n", ttyout); 1863 code = -1; 1864 return; 1865 } 1866 if (!c->c_proxy) { 1867 fputs("?Invalid proxy command.\n", ttyout); 1868 code = -1; 1869 return; 1870 } 1871 if (sigsetjmp(abortprox, 1)) { 1872 code = -1; 1873 return; 1874 } 1875 oldintr = xsignal(SIGINT, proxabort); 1876 pswitch(1); 1877 if (c->c_conn && !connected) { 1878 fputs("Not connected.\n", ttyout); 1879 pswitch(0); 1880 (void)xsignal(SIGINT, oldintr); 1881 code = -1; 1882 return; 1883 } 1884 cmdpos = strcspn(line, " \t"); 1885 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1886 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1887 (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); 1888 argv[1] = cmdbuf; 1889 (*c->c_handler)(argc-1, argv+1); 1890 if (connected) { 1891 proxflag = 1; 1892 } 1893 else { 1894 proxflag = 0; 1895 } 1896 pswitch(0); 1897 (void)xsignal(SIGINT, oldintr); 1898 } 1899 1900 void 1901 setcase(int argc, char *argv[]) 1902 { 1903 1904 code = togglevar(argc, argv, &mcase, "Case mapping"); 1905 } 1906 1907 /* 1908 * convert the given name to lower case if it's all upper case, into 1909 * a static buffer which is returned to the caller 1910 */ 1911 static const char * 1912 docase(char *dst, size_t dlen, const char *src) 1913 { 1914 size_t i; 1915 int dochange = 1; 1916 1917 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) { 1918 dst[i] = src[i]; 1919 if (islower((unsigned char)dst[i])) 1920 dochange = 0; 1921 } 1922 dst[i] = '\0'; 1923 1924 if (dochange) { 1925 for (i = 0; dst[i] != '\0'; i++) 1926 if (isupper((unsigned char)dst[i])) 1927 dst[i] = tolower((unsigned char)dst[i]); 1928 } 1929 return dst; 1930 } 1931 1932 void 1933 setcr(int argc, char *argv[]) 1934 { 1935 1936 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1937 } 1938 1939 void 1940 setntrans(int argc, char *argv[]) 1941 { 1942 1943 if (argc == 0 || argc > 3) { 1944 UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]); 1945 code = -1; 1946 return; 1947 } 1948 if (argc == 1) { 1949 ntflag = 0; 1950 fputs("Ntrans off.\n", ttyout); 1951 code = ntflag; 1952 return; 1953 } 1954 ntflag++; 1955 code = ntflag; 1956 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1957 if (argc == 2) { 1958 ntout[0] = '\0'; 1959 return; 1960 } 1961 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1962 } 1963 1964 static const char * 1965 dotrans(char *dst, size_t dlen, const char *src) 1966 { 1967 const char *cp1; 1968 char *cp2 = dst; 1969 size_t i, ostop; 1970 1971 for (ostop = 0; ntout[ostop] && ostop < sizeof(ntout); ostop++) 1972 continue; 1973 for (cp1 = src; *cp1; cp1++) { 1974 int found = 0; 1975 for (i = 0; i < sizeof(ntin) && ntin[i]; i++) { 1976 if (*cp1 == ntin[i]) { 1977 found++; 1978 if (i < ostop) { 1979 *cp2++ = ntout[i]; 1980 if (cp2 - dst >= (ptrdiff_t)(dlen - 1)) 1981 goto out; 1982 } 1983 break; 1984 } 1985 } 1986 if (!found) { 1987 *cp2++ = *cp1; 1988 } 1989 } 1990 out: 1991 *cp2 = '\0'; 1992 return dst; 1993 } 1994 1995 void 1996 setnmap(int argc, char *argv[]) 1997 { 1998 char *cp; 1999 2000 if (argc == 1) { 2001 mapflag = 0; 2002 fputs("Nmap off.\n", ttyout); 2003 code = mapflag; 2004 return; 2005 } 2006 if (argc == 0 || 2007 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { 2008 UPRINTF("usage: %s [mapin mapout]\n", argv[0]); 2009 code = -1; 2010 return; 2011 } 2012 mapflag = 1; 2013 code = 1; 2014 cp = strchr(altarg, ' '); 2015 if (proxy) { 2016 while(*++cp == ' ') 2017 continue; 2018 altarg = cp; 2019 cp = strchr(altarg, ' '); 2020 } 2021 *cp = '\0'; 2022 (void)strlcpy(mapin, altarg, MAXPATHLEN); 2023 while (*++cp == ' ') 2024 continue; 2025 (void)strlcpy(mapout, cp, MAXPATHLEN); 2026 } 2027 2028 static const char * 2029 domap(char *dst, size_t dlen, const char *src) 2030 { 2031 const char *cp1 = src; 2032 char *cp2 = mapin; 2033 const char *tp[9], *te[9]; 2034 int i, toks[9], toknum = 0, match = 1; 2035 2036 for (i=0; i < 9; ++i) { 2037 toks[i] = 0; 2038 } 2039 while (match && *cp1 && *cp2) { 2040 switch (*cp2) { 2041 case '\\': 2042 if (*++cp2 != *cp1) { 2043 match = 0; 2044 } 2045 break; 2046 case '$': 2047 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 2048 if (*cp1 != *(++cp2+1)) { 2049 toks[toknum = *cp2 - '1']++; 2050 tp[toknum] = cp1; 2051 while (*++cp1 && *(cp2+1) 2052 != *cp1); 2053 te[toknum] = cp1; 2054 } 2055 cp2++; 2056 break; 2057 } 2058 /* FALLTHROUGH */ 2059 default: 2060 if (*cp2 != *cp1) { 2061 match = 0; 2062 } 2063 break; 2064 } 2065 if (match && *cp1) { 2066 cp1++; 2067 } 2068 if (match && *cp2) { 2069 cp2++; 2070 } 2071 } 2072 if (!match && *cp1) /* last token mismatch */ 2073 { 2074 toks[toknum] = 0; 2075 } 2076 cp2 = dst; 2077 *cp2 = '\0'; 2078 cp1 = mapout; 2079 while (*cp1) { 2080 match = 0; 2081 switch (*cp1) { 2082 case '\\': 2083 if (*(cp1 + 1)) { 2084 *cp2++ = *++cp1; 2085 } 2086 break; 2087 case '[': 2088 LOOP: 2089 if (*++cp1 == '$' && 2090 isdigit((unsigned char)*(cp1+1))) { 2091 if (*++cp1 == '0') { 2092 const char *cp3 = src; 2093 2094 while (*cp3) { 2095 *cp2++ = *cp3++; 2096 } 2097 match = 1; 2098 } 2099 else if (toks[toknum = *cp1 - '1']) { 2100 const char *cp3 = tp[toknum]; 2101 2102 while (cp3 != te[toknum]) { 2103 *cp2++ = *cp3++; 2104 } 2105 match = 1; 2106 } 2107 } 2108 else { 2109 while (*cp1 && *cp1 != ',' && 2110 *cp1 != ']') { 2111 if (*cp1 == '\\') { 2112 cp1++; 2113 } 2114 else if (*cp1 == '$' && 2115 isdigit((unsigned char)*(cp1+1))) { 2116 if (*++cp1 == '0') { 2117 const char *cp3 = src; 2118 2119 while (*cp3) { 2120 *cp2++ = *cp3++; 2121 } 2122 } 2123 else if (toks[toknum = 2124 *cp1 - '1']) { 2125 const char *cp3=tp[toknum]; 2126 2127 while (cp3 != 2128 te[toknum]) { 2129 *cp2++ = *cp3++; 2130 } 2131 } 2132 } 2133 else if (*cp1) { 2134 *cp2++ = *cp1++; 2135 } 2136 } 2137 if (!*cp1) { 2138 fputs( 2139 "nmap: unbalanced brackets.\n", 2140 ttyout); 2141 return (src); 2142 } 2143 match = 1; 2144 cp1--; 2145 } 2146 if (match) { 2147 while (*++cp1 && *cp1 != ']') { 2148 if (*cp1 == '\\' && *(cp1 + 1)) { 2149 cp1++; 2150 } 2151 } 2152 if (!*cp1) { 2153 fputs( 2154 "nmap: unbalanced brackets.\n", 2155 ttyout); 2156 return (src); 2157 } 2158 break; 2159 } 2160 switch (*++cp1) { 2161 case ',': 2162 goto LOOP; 2163 case ']': 2164 break; 2165 default: 2166 cp1--; 2167 goto LOOP; 2168 } 2169 break; 2170 case '$': 2171 if (isdigit((unsigned char)*(cp1 + 1))) { 2172 if (*++cp1 == '0') { 2173 const char *cp3 = src; 2174 2175 while (*cp3) { 2176 *cp2++ = *cp3++; 2177 } 2178 } 2179 else if (toks[toknum = *cp1 - '1']) { 2180 const char *cp3 = tp[toknum]; 2181 2182 while (cp3 != te[toknum]) { 2183 *cp2++ = *cp3++; 2184 } 2185 } 2186 break; 2187 } 2188 /* intentional drop through */ 2189 default: 2190 *cp2++ = *cp1; 2191 break; 2192 } 2193 cp1++; 2194 } 2195 *cp2 = '\0'; 2196 return *dst ? dst : src; 2197 } 2198 2199 void 2200 setpassive(int argc, char *argv[]) 2201 { 2202 2203 if (argc == 1) { 2204 passivemode = !passivemode; 2205 activefallback = passivemode; 2206 } else if (argc != 2) { 2207 passiveusage: 2208 UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]); 2209 code = -1; 2210 return; 2211 } else if (strcasecmp(argv[1], "on") == 0) { 2212 passivemode = 1; 2213 activefallback = 0; 2214 } else if (strcasecmp(argv[1], "off") == 0) { 2215 passivemode = 0; 2216 activefallback = 0; 2217 } else if (strcasecmp(argv[1], "auto") == 0) { 2218 passivemode = 1; 2219 activefallback = 1; 2220 } else 2221 goto passiveusage; 2222 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 2223 onoff(passivemode), onoff(activefallback)); 2224 code = passivemode; 2225 } 2226 2227 2228 void 2229 setepsv4(int argc, char *argv[]) 2230 { 2231 code = togglevar(argc, argv, &epsv4, 2232 verbose ? "EPSV/EPRT on IPv4" : NULL); 2233 epsv4bad = 0; 2234 } 2235 2236 void 2237 setepsv6(int argc, char *argv[]) 2238 { 2239 code = togglevar(argc, argv, &epsv6, 2240 verbose ? "EPSV/EPRT on IPv6" : NULL); 2241 epsv6bad = 0; 2242 } 2243 2244 void 2245 setepsv(int argc, char*argv[]) 2246 { 2247 setepsv4(argc,argv); 2248 setepsv6(argc,argv); 2249 } 2250 2251 void 2252 setsunique(int argc, char *argv[]) 2253 { 2254 2255 code = togglevar(argc, argv, &sunique, "Store unique"); 2256 } 2257 2258 void 2259 setrunique(int argc, char *argv[]) 2260 { 2261 2262 code = togglevar(argc, argv, &runique, "Receive unique"); 2263 } 2264 2265 int 2266 parserate(int argc, char *argv[], int cmdlineopt) 2267 { 2268 int dir, max, incr, showonly; 2269 sigfunc oldusr1, oldusr2; 2270 2271 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { 2272 usage: 2273 if (cmdlineopt) 2274 UPRINTF( 2275 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", 2276 argv[0]); 2277 else 2278 UPRINTF( 2279 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", 2280 argv[0]); 2281 return -1; 2282 } 2283 dir = max = incr = showonly = 0; 2284 #define RATE_GET 1 2285 #define RATE_PUT 2 2286 #define RATE_ALL (RATE_GET | RATE_PUT) 2287 2288 if (strcasecmp(argv[1], "all") == 0) 2289 dir = RATE_ALL; 2290 else if (strcasecmp(argv[1], "get") == 0) 2291 dir = RATE_GET; 2292 else if (strcasecmp(argv[1], "put") == 0) 2293 dir = RATE_PUT; 2294 else 2295 goto usage; 2296 2297 if (argc >= 3) { 2298 if ((max = strsuftoi(argv[2])) < 0) 2299 goto usage; 2300 } else 2301 showonly = 1; 2302 2303 if (argc == 4) { 2304 if ((incr = strsuftoi(argv[3])) <= 0) 2305 goto usage; 2306 } else 2307 incr = DEFAULTINCR; 2308 2309 oldusr1 = xsignal(SIGUSR1, SIG_IGN); 2310 oldusr2 = xsignal(SIGUSR2, SIG_IGN); 2311 if (dir & RATE_GET) { 2312 if (!showonly) { 2313 rate_get = max; 2314 rate_get_incr = incr; 2315 } 2316 if (!cmdlineopt || verbose) 2317 fprintf(ttyout, 2318 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 2319 onoff(rate_get), rate_get, rate_get_incr); 2320 } 2321 if (dir & RATE_PUT) { 2322 if (!showonly) { 2323 rate_put = max; 2324 rate_put_incr = incr; 2325 } 2326 if (!cmdlineopt || verbose) 2327 fprintf(ttyout, 2328 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 2329 onoff(rate_put), rate_put, rate_put_incr); 2330 } 2331 (void)xsignal(SIGUSR1, oldusr1); 2332 (void)xsignal(SIGUSR2, oldusr2); 2333 return 0; 2334 } 2335 2336 void 2337 setrate(int argc, char *argv[]) 2338 { 2339 2340 code = parserate(argc, argv, 0); 2341 } 2342 2343 /* change directory to parent directory */ 2344 void 2345 cdup(int argc, char *argv[]) 2346 { 2347 int r; 2348 2349 if (argc == 0) { 2350 UPRINTF("usage: %s\n", argv[0]); 2351 code = -1; 2352 return; 2353 } 2354 r = command("CDUP"); 2355 if (r == ERROR && code == 500) { 2356 if (verbose) 2357 fputs("CDUP command not recognized, trying XCUP.\n", 2358 ttyout); 2359 r = command("XCUP"); 2360 } 2361 if (r == COMPLETE) { 2362 dirchange = 1; 2363 remotecwd[0] = '\0'; 2364 remcwdvalid = 0; 2365 } 2366 } 2367 2368 /* 2369 * Restart transfer at specific point 2370 */ 2371 void 2372 restart(int argc, char *argv[]) 2373 { 2374 2375 if (argc == 0 || argc > 2) { 2376 UPRINTF("usage: %s [restart-point]\n", argv[0]); 2377 code = -1; 2378 return; 2379 } 2380 if (! features[FEAT_REST_STREAM]) { 2381 fprintf(ttyout, 2382 "Restart is not supported by the remote server.\n"); 2383 return; 2384 } 2385 if (argc == 2) { 2386 off_t rp; 2387 char *ep; 2388 2389 rp = STRTOLL(argv[1], &ep, 10); 2390 if (rp < 0 || *ep != '\0') 2391 fprintf(ttyout, "restart: Invalid offset `%s'\n", 2392 argv[1]); 2393 else 2394 restart_point = rp; 2395 } 2396 if (restart_point == 0) 2397 fputs("No restart point defined.\n", ttyout); 2398 else 2399 fprintf(ttyout, 2400 "Restarting at " LLF " for next get, put or append\n", 2401 (LLT)restart_point); 2402 } 2403 2404 /* 2405 * Show remote system type 2406 */ 2407 void 2408 syst(int argc, char *argv[]) 2409 { 2410 int oldverbose = verbose; 2411 2412 if (argc == 0) { 2413 UPRINTF("usage: %s\n", argv[0]); 2414 code = -1; 2415 return; 2416 } 2417 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2418 (void)command("SYST"); 2419 verbose = oldverbose; 2420 } 2421 2422 void 2423 macdef(int argc, char *argv[]) 2424 { 2425 char *tmp; 2426 int c; 2427 2428 if (argc == 0) 2429 goto usage; 2430 if (macnum == 16) { 2431 fputs("Limit of 16 macros have already been defined.\n", 2432 ttyout); 2433 code = -1; 2434 return; 2435 } 2436 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { 2437 usage: 2438 UPRINTF("usage: %s macro_name\n", argv[0]); 2439 code = -1; 2440 return; 2441 } 2442 if (interactive) 2443 fputs( 2444 "Enter macro line by line, terminating it with a null line.\n", 2445 ttyout); 2446 (void)strlcpy(macros[macnum].mac_name, argv[1], 2447 sizeof(macros[macnum].mac_name)); 2448 if (macnum == 0) 2449 macros[macnum].mac_start = macbuf; 2450 else 2451 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2452 tmp = macros[macnum].mac_start; 2453 while (tmp != macbuf+4096) { 2454 if ((c = getchar()) == EOF) { 2455 fputs("macdef: end of file encountered.\n", ttyout); 2456 code = -1; 2457 return; 2458 } 2459 if ((*tmp = c) == '\n') { 2460 if (tmp == macros[macnum].mac_start) { 2461 macros[macnum++].mac_end = tmp; 2462 code = 0; 2463 return; 2464 } 2465 if (*(tmp-1) == '\0') { 2466 macros[macnum++].mac_end = tmp - 1; 2467 code = 0; 2468 return; 2469 } 2470 *tmp = '\0'; 2471 } 2472 tmp++; 2473 } 2474 while (1) { 2475 while ((c = getchar()) != '\n' && c != EOF) 2476 /* LOOP */; 2477 if (c == EOF || getchar() == '\n') { 2478 fputs("Macro not defined - 4K buffer exceeded.\n", 2479 ttyout); 2480 code = -1; 2481 return; 2482 } 2483 } 2484 } 2485 2486 /* 2487 * Get size of file on remote machine 2488 */ 2489 void 2490 sizecmd(int argc, char *argv[]) 2491 { 2492 off_t size; 2493 2494 if (argc == 0 || argc > 2 || 2495 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2496 UPRINTF("usage: %s remote-file\n", argv[0]); 2497 code = -1; 2498 return; 2499 } 2500 size = remotesize(argv[1], 1); 2501 if (size != -1) 2502 fprintf(ttyout, 2503 "%s\t" LLF "\n", argv[1], (LLT)size); 2504 code = (size > 0); 2505 } 2506 2507 /* 2508 * Get last modification time of file on remote machine 2509 */ 2510 void 2511 modtime(int argc, char *argv[]) 2512 { 2513 time_t mtime; 2514 2515 if (argc == 0 || argc > 2 || 2516 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2517 UPRINTF("usage: %s remote-file\n", argv[0]); 2518 code = -1; 2519 return; 2520 } 2521 mtime = remotemodtime(argv[1], 1); 2522 if (mtime != -1) 2523 fprintf(ttyout, "%s\t%s", argv[1], 2524 rfc2822time(localtime(&mtime))); 2525 code = (mtime > 0); 2526 } 2527 2528 /* 2529 * Show status on remote machine 2530 */ 2531 void 2532 rmtstatus(int argc, char *argv[]) 2533 { 2534 2535 if (argc == 0) { 2536 UPRINTF("usage: %s [remote-file]\n", argv[0]); 2537 code = -1; 2538 return; 2539 } 2540 COMMAND_1ARG(argc, argv, "STAT"); 2541 } 2542 2543 /* 2544 * Get file if modtime is more recent than current file 2545 */ 2546 void 2547 newer(int argc, char *argv[]) 2548 { 2549 2550 if (getit(argc, argv, -1, "w")) 2551 fprintf(ttyout, 2552 "Local file \"%s\" is newer than remote file \"%s\".\n", 2553 argv[2], argv[1]); 2554 } 2555 2556 /* 2557 * Display one local file through $PAGER. 2558 */ 2559 void 2560 lpage(int argc, char *argv[]) 2561 { 2562 size_t len; 2563 const char *p; 2564 char *pager, *locfile; 2565 2566 if (argc == 0 || argc > 2 || 2567 (argc == 1 && !another(&argc, &argv, "local-file"))) { 2568 UPRINTF("usage: %s local-file\n", argv[0]); 2569 code = -1; 2570 return; 2571 } 2572 if ((locfile = globulize(argv[1])) == NULL) { 2573 code = -1; 2574 return; 2575 } 2576 p = getoptionvalue("pager"); 2577 if (EMPTYSTRING(p)) 2578 p = DEFAULTPAGER; 2579 len = strlen(p) + strlen(locfile) + 2; 2580 pager = ftp_malloc(len); 2581 (void)strlcpy(pager, p, len); 2582 (void)strlcat(pager, " ", len); 2583 (void)strlcat(pager, locfile, len); 2584 system(pager); 2585 code = 0; 2586 (void)free(pager); 2587 (void)free(locfile); 2588 } 2589 2590 /* 2591 * Display one remote file through $PAGER. 2592 */ 2593 void 2594 page(int argc, char *argv[]) 2595 { 2596 int ohash, orestart_point, overbose; 2597 size_t len; 2598 const char *p; 2599 char *pager; 2600 2601 if (argc == 0 || argc > 2 || 2602 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2603 UPRINTF("usage: %s remote-file\n", argv[0]); 2604 code = -1; 2605 return; 2606 } 2607 p = getoptionvalue("pager"); 2608 if (EMPTYSTRING(p)) 2609 p = DEFAULTPAGER; 2610 len = strlen(p) + 2; 2611 pager = ftp_malloc(len); 2612 pager[0] = '|'; 2613 (void)strlcpy(pager + 1, p, len - 1); 2614 2615 ohash = hash; 2616 orestart_point = restart_point; 2617 overbose = verbose; 2618 hash = restart_point = verbose = 0; 2619 recvrequest("RETR", pager, argv[1], "r+", 1, 0); 2620 hash = ohash; 2621 restart_point = orestart_point; 2622 verbose = overbose; 2623 (void)free(pager); 2624 } 2625 2626 /* 2627 * Set the socket send or receive buffer size. 2628 */ 2629 void 2630 setxferbuf(int argc, char *argv[]) 2631 { 2632 int size, dir; 2633 2634 if (argc != 2) { 2635 usage: 2636 UPRINTF("usage: %s size\n", argv[0]); 2637 code = -1; 2638 return; 2639 } 2640 if (strcasecmp(argv[0], "sndbuf") == 0) 2641 dir = RATE_PUT; 2642 else if (strcasecmp(argv[0], "rcvbuf") == 0) 2643 dir = RATE_GET; 2644 else if (strcasecmp(argv[0], "xferbuf") == 0) 2645 dir = RATE_ALL; 2646 else 2647 goto usage; 2648 2649 if ((size = strsuftoi(argv[1])) == -1) 2650 goto usage; 2651 2652 if (size == 0) { 2653 fprintf(ttyout, "%s: size must be positive.\n", argv[0]); 2654 goto usage; 2655 } 2656 2657 if (dir & RATE_PUT) 2658 sndbuf_size = size; 2659 if (dir & RATE_GET) 2660 rcvbuf_size = size; 2661 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n", 2662 sndbuf_size, rcvbuf_size); 2663 code = 0; 2664 } 2665 2666 /* 2667 * Set or display options (defaults are provided by various env vars) 2668 */ 2669 void 2670 setoption(int argc, char *argv[]) 2671 { 2672 struct option *o; 2673 2674 code = -1; 2675 if (argc == 0 || (argc != 1 && argc != 3)) { 2676 UPRINTF("usage: %s [option value]\n", argv[0]); 2677 return; 2678 } 2679 2680 #define OPTIONINDENT ((int) sizeof("https_proxy")) 2681 if (argc == 1) { 2682 for (o = optiontab; o->name != NULL; o++) { 2683 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, 2684 o->name, o->value ? o->value : ""); 2685 } 2686 } else { 2687 set_option(argv[1], argv[2], 1); 2688 } 2689 code = 0; 2690 } 2691 2692 void 2693 set_option(const char * option, const char * value, int doverbose) 2694 { 2695 struct option *o; 2696 2697 o = getoption(option); 2698 if (o == NULL) { 2699 fprintf(ttyout, "No such option `%s'.\n", option); 2700 return; 2701 } 2702 FREEPTR(o->value); 2703 o->value = ftp_strdup(value); 2704 if (verbose && doverbose) 2705 fprintf(ttyout, "Setting `%s' to `%s'.\n", 2706 o->name, o->value); 2707 } 2708 2709 /* 2710 * Unset an option 2711 */ 2712 void 2713 unsetoption(int argc, char *argv[]) 2714 { 2715 struct option *o; 2716 2717 code = -1; 2718 if (argc == 0 || argc != 2) { 2719 UPRINTF("usage: %s option\n", argv[0]); 2720 return; 2721 } 2722 2723 o = getoption(argv[1]); 2724 if (o == NULL) { 2725 fprintf(ttyout, "No such option `%s'.\n", argv[1]); 2726 return; 2727 } 2728 FREEPTR(o->value); 2729 fprintf(ttyout, "Unsetting `%s'.\n", o->name); 2730 code = 0; 2731 } 2732 2733 /* 2734 * Display features supported by the remote host. 2735 */ 2736 void 2737 feat(int argc, char *argv[]) 2738 { 2739 int oldverbose = verbose; 2740 2741 if (argc == 0) { 2742 UPRINTF("usage: %s\n", argv[0]); 2743 code = -1; 2744 return; 2745 } 2746 if (! features[FEAT_FEAT]) { 2747 fprintf(ttyout, 2748 "FEAT is not supported by the remote server.\n"); 2749 return; 2750 } 2751 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2752 (void)command("FEAT"); 2753 verbose = oldverbose; 2754 } 2755 2756 void 2757 mlst(int argc, char *argv[]) 2758 { 2759 int oldverbose = verbose; 2760 2761 if (argc < 1 || argc > 2) { 2762 UPRINTF("usage: %s [remote-path]\n", argv[0]); 2763 code = -1; 2764 return; 2765 } 2766 if (! features[FEAT_MLST]) { 2767 fprintf(ttyout, 2768 "MLST is not supported by the remote server.\n"); 2769 return; 2770 } 2771 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2772 COMMAND_1ARG(argc, argv, "MLST"); 2773 verbose = oldverbose; 2774 } 2775 2776 void 2777 opts(int argc, char *argv[]) 2778 { 2779 int oldverbose = verbose; 2780 2781 if (argc < 2 || argc > 3) { 2782 UPRINTF("usage: %s command [options]\n", argv[0]); 2783 code = -1; 2784 return; 2785 } 2786 if (! features[FEAT_FEAT]) { 2787 fprintf(ttyout, 2788 "OPTS is not supported by the remote server.\n"); 2789 return; 2790 } 2791 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2792 if (argc == 2) 2793 command("OPTS %s", argv[1]); 2794 else 2795 command("OPTS %s %s", argv[1], argv[2]); 2796 verbose = oldverbose; 2797 } 2798