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