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