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