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