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