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