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