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 (void)closefrom(3); 1442 (void)xsignal(SIGINT, SIG_DFL); 1443 shellp = getenv("SHELL"); 1444 if (shellp == NULL) 1445 shellp = _PATH_BSHELL; 1446 namep = strrchr(shellp, '/'); 1447 if (namep == NULL) 1448 namep = shellp; 1449 else 1450 namep++; 1451 (void)strlcpy(shellnam, namep, sizeof(shellnam)); 1452 if (ftp_debug) { 1453 fputs(shellp, ttyout); 1454 putc('\n', ttyout); 1455 } 1456 if (argc > 1) { 1457 execl(shellp, shellnam, "-c", altarg, (char *)0); 1458 } 1459 else { 1460 execl(shellp, shellnam, (char *)0); 1461 } 1462 warn("Can't execute `%s'", shellp); 1463 code = -1; 1464 exit(1); 1465 } 1466 if (pid > 0) 1467 while (wait(&wait_status) != pid) 1468 ; 1469 (void)xsignal(SIGINT, oldintr); 1470 if (pid == -1) { 1471 warn("Can't fork a subshell; try again later"); 1472 code = -1; 1473 } else 1474 code = 0; 1475 } 1476 1477 /* 1478 * Send new user information (re-login) 1479 */ 1480 void 1481 user(int argc, char *argv[]) 1482 { 1483 char *password; 1484 char emptypass[] = ""; 1485 int n, aflag = 0; 1486 1487 if (argc == 0) 1488 goto usage; 1489 if (argc < 2) 1490 (void)another(&argc, &argv, "username"); 1491 if (argc < 2 || argc > 4) { 1492 usage: 1493 UPRINTF("usage: %s username [password [account]]\n", 1494 argv[0]); 1495 code = -1; 1496 return; 1497 } 1498 n = command("USER %s", argv[1]); 1499 if (n == CONTINUE) { 1500 if (argc < 3) { 1501 password = getpass("Password: "); 1502 if (password == NULL) 1503 password = emptypass; 1504 } else { 1505 password = argv[2]; 1506 } 1507 n = command("PASS %s", password); 1508 memset(password, 0, strlen(password)); 1509 } 1510 if (n == CONTINUE) { 1511 aflag++; 1512 if (argc < 4) { 1513 password = getpass("Account: "); 1514 if (password == NULL) 1515 password = emptypass; 1516 } else { 1517 password = argv[3]; 1518 } 1519 n = command("ACCT %s", password); 1520 memset(password, 0, strlen(password)); 1521 } 1522 if (n != COMPLETE) { 1523 fputs("Login failed.\n", ttyout); 1524 return; 1525 } 1526 if (!aflag && argc == 4) { 1527 password = argv[3]; 1528 (void)command("ACCT %s", password); 1529 memset(password, 0, strlen(password)); 1530 } 1531 connected = -1; 1532 getremoteinfo(); 1533 } 1534 1535 /* 1536 * Print working directory on remote machine. 1537 */ 1538 /*VARARGS*/ 1539 void 1540 pwd(int argc, char *argv[]) 1541 { 1542 1543 code = -1; 1544 if (argc != 1) { 1545 UPRINTF("usage: %s\n", argv[0]); 1546 return; 1547 } 1548 if (! remotecwd[0]) 1549 updateremotecwd(); 1550 if (! remotecwd[0]) 1551 fprintf(ttyout, "Unable to determine remote directory\n"); 1552 else { 1553 fprintf(ttyout, "Remote directory: %s\n", remotecwd); 1554 code = 0; 1555 } 1556 } 1557 1558 /* 1559 * Print working directory on local machine. 1560 */ 1561 void 1562 lpwd(int argc, char *argv[]) 1563 { 1564 1565 code = -1; 1566 if (argc != 1) { 1567 UPRINTF("usage: %s\n", argv[0]); 1568 return; 1569 } 1570 if (! localcwd[0]) 1571 updatelocalcwd(); 1572 if (! localcwd[0]) 1573 fprintf(ttyout, "Unable to determine local directory\n"); 1574 else { 1575 fprintf(ttyout, "Local directory: %s\n", localcwd); 1576 code = 0; 1577 } 1578 } 1579 1580 /* 1581 * Make a directory. 1582 */ 1583 void 1584 makedir(int argc, char *argv[]) 1585 { 1586 int r; 1587 1588 if (argc == 0 || argc > 2 || 1589 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1590 UPRINTF("usage: %s directory-name\n", argv[0]); 1591 code = -1; 1592 return; 1593 } 1594 r = command("MKD %s", argv[1]); 1595 if (r == ERROR && code == 500) { 1596 if (verbose) 1597 fputs("MKD command not recognized, trying XMKD.\n", 1598 ttyout); 1599 r = command("XMKD %s", argv[1]); 1600 } 1601 if (r == COMPLETE) 1602 dirchange = 1; 1603 } 1604 1605 /* 1606 * Remove a directory. 1607 */ 1608 void 1609 removedir(int argc, char *argv[]) 1610 { 1611 int r; 1612 1613 if (argc == 0 || argc > 2 || 1614 (argc == 1 && !another(&argc, &argv, "directory-name"))) { 1615 UPRINTF("usage: %s directory-name\n", argv[0]); 1616 code = -1; 1617 return; 1618 } 1619 r = command("RMD %s", argv[1]); 1620 if (r == ERROR && code == 500) { 1621 if (verbose) 1622 fputs("RMD command not recognized, trying XRMD.\n", 1623 ttyout); 1624 r = command("XRMD %s", argv[1]); 1625 } 1626 if (r == COMPLETE) 1627 dirchange = 1; 1628 } 1629 1630 /* 1631 * Send a line, verbatim, to the remote machine. 1632 */ 1633 void 1634 quote(int argc, char *argv[]) 1635 { 1636 1637 if (argc == 0 || 1638 (argc == 1 && !another(&argc, &argv, "command line to send"))) { 1639 UPRINTF("usage: %s line-to-send\n", argv[0]); 1640 code = -1; 1641 return; 1642 } 1643 quote1("", argc, argv); 1644 } 1645 1646 /* 1647 * Send a SITE command to the remote machine. The line 1648 * is sent verbatim to the remote machine, except that the 1649 * word "SITE" is added at the front. 1650 */ 1651 void 1652 site(int argc, char *argv[]) 1653 { 1654 1655 if (argc == 0 || 1656 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){ 1657 UPRINTF("usage: %s line-to-send\n", argv[0]); 1658 code = -1; 1659 return; 1660 } 1661 quote1("SITE ", argc, argv); 1662 } 1663 1664 /* 1665 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1666 * Send the result as a one-line command and get response. 1667 */ 1668 void 1669 quote1(const char *initial, int argc, char *argv[]) 1670 { 1671 int i; 1672 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1673 1674 (void)strlcpy(buf, initial, sizeof(buf)); 1675 for (i = 1; i < argc; i++) { 1676 (void)strlcat(buf, argv[i], sizeof(buf)); 1677 if (i < (argc - 1)) 1678 (void)strlcat(buf, " ", sizeof(buf)); 1679 } 1680 if (command("%s", buf) == PRELIM) { 1681 while (getreply(0) == PRELIM) 1682 continue; 1683 } 1684 dirchange = 1; 1685 } 1686 1687 void 1688 do_chmod(int argc, char *argv[]) 1689 { 1690 1691 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode"))) 1692 goto usage; 1693 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 1694 usage: 1695 UPRINTF("usage: %s mode remote-file\n", argv[0]); 1696 code = -1; 1697 return; 1698 } 1699 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1700 } 1701 1702 #define COMMAND_1ARG(argc, argv, cmd) \ 1703 if (argc == 1) \ 1704 command(cmd); \ 1705 else \ 1706 command(cmd " %s", argv[1]) 1707 1708 void 1709 do_umask(int argc, char *argv[]) 1710 { 1711 int oldverbose = verbose; 1712 1713 if (argc == 0) { 1714 UPRINTF("usage: %s [umask]\n", argv[0]); 1715 code = -1; 1716 return; 1717 } 1718 verbose = 1; 1719 COMMAND_1ARG(argc, argv, "SITE UMASK"); 1720 verbose = oldverbose; 1721 } 1722 1723 void 1724 idlecmd(int argc, char *argv[]) 1725 { 1726 int oldverbose = verbose; 1727 1728 if (argc < 1 || argc > 2) { 1729 UPRINTF("usage: %s [seconds]\n", argv[0]); 1730 code = -1; 1731 return; 1732 } 1733 verbose = 1; 1734 COMMAND_1ARG(argc, argv, "SITE IDLE"); 1735 verbose = oldverbose; 1736 } 1737 1738 /* 1739 * Ask the other side for help. 1740 */ 1741 void 1742 rmthelp(int argc, char *argv[]) 1743 { 1744 int oldverbose = verbose; 1745 1746 if (argc == 0) { 1747 UPRINTF("usage: %s\n", argv[0]); 1748 code = -1; 1749 return; 1750 } 1751 verbose = 1; 1752 COMMAND_1ARG(argc, argv, "HELP"); 1753 verbose = oldverbose; 1754 } 1755 1756 /* 1757 * Terminate session and exit. 1758 * May be called with 0, NULL. 1759 */ 1760 /*VARARGS*/ 1761 void 1762 quit(int argc, char *argv[]) 1763 { 1764 1765 /* this may be called with argc == 0, argv == NULL */ 1766 if (argc == 0 && argv != NULL) { 1767 UPRINTF("usage: %s\n", argv[0]); 1768 code = -1; 1769 return; 1770 } 1771 if (connected) 1772 disconnect(0, NULL); 1773 pswitch(1); 1774 if (connected) 1775 disconnect(0, NULL); 1776 exit(0); 1777 } 1778 1779 /* 1780 * Terminate session, but don't exit. 1781 * May be called with 0, NULL. 1782 */ 1783 void 1784 disconnect(int argc, char *argv[]) 1785 { 1786 1787 /* this may be called with argc == 0, argv == NULL */ 1788 if (argc == 0 && argv != NULL) { 1789 UPRINTF("usage: %s\n", argv[0]); 1790 code = -1; 1791 return; 1792 } 1793 if (!connected) 1794 return; 1795 (void)command("QUIT"); 1796 cleanuppeer(); 1797 } 1798 1799 void 1800 account(int argc, char *argv[]) 1801 { 1802 char *ap; 1803 char emptypass[] = ""; 1804 1805 if (argc == 0 || argc > 2) { 1806 UPRINTF("usage: %s [password]\n", argv[0]); 1807 code = -1; 1808 return; 1809 } 1810 else if (argc == 2) 1811 ap = argv[1]; 1812 else { 1813 ap = getpass("Account:"); 1814 if (ap == NULL) 1815 ap = emptypass; 1816 } 1817 (void)command("ACCT %s", ap); 1818 memset(ap, 0, strlen(ap)); 1819 } 1820 1821 sigjmp_buf abortprox; 1822 1823 void 1824 proxabort(int notused) 1825 { 1826 1827 sigint_raised = 1; 1828 alarmtimer(0); 1829 if (!proxy) { 1830 pswitch(1); 1831 } 1832 if (connected) { 1833 proxflag = 1; 1834 } 1835 else { 1836 proxflag = 0; 1837 } 1838 pswitch(0); 1839 siglongjmp(abortprox, 1); 1840 } 1841 1842 void 1843 doproxy(int argc, char *argv[]) 1844 { 1845 struct cmd *c; 1846 int cmdpos; 1847 sigfunc oldintr; 1848 char cmdbuf[MAX_C_NAME]; 1849 1850 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { 1851 UPRINTF("usage: %s command\n", argv[0]); 1852 code = -1; 1853 return; 1854 } 1855 c = getcmd(argv[1]); 1856 if (c == (struct cmd *) -1) { 1857 fputs("?Ambiguous command.\n", ttyout); 1858 code = -1; 1859 return; 1860 } 1861 if (c == 0) { 1862 fputs("?Invalid command.\n", ttyout); 1863 code = -1; 1864 return; 1865 } 1866 if (!c->c_proxy) { 1867 fputs("?Invalid proxy command.\n", ttyout); 1868 code = -1; 1869 return; 1870 } 1871 if (sigsetjmp(abortprox, 1)) { 1872 code = -1; 1873 return; 1874 } 1875 oldintr = xsignal(SIGINT, proxabort); 1876 pswitch(1); 1877 if (c->c_conn && !connected) { 1878 fputs("Not connected.\n", ttyout); 1879 pswitch(0); 1880 (void)xsignal(SIGINT, oldintr); 1881 code = -1; 1882 return; 1883 } 1884 cmdpos = strcspn(line, " \t"); 1885 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1886 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1887 (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); 1888 argv[1] = cmdbuf; 1889 (*c->c_handler)(argc-1, argv+1); 1890 if (connected) { 1891 proxflag = 1; 1892 } 1893 else { 1894 proxflag = 0; 1895 } 1896 pswitch(0); 1897 (void)xsignal(SIGINT, oldintr); 1898 } 1899 1900 void 1901 setcase(int argc, char *argv[]) 1902 { 1903 1904 code = togglevar(argc, argv, &mcase, "Case mapping"); 1905 } 1906 1907 /* 1908 * convert the given name to lower case if it's all upper case, into 1909 * a static buffer which is returned to the caller 1910 */ 1911 static const char * 1912 docase(char *dst, size_t dlen, const char *src) 1913 { 1914 size_t i; 1915 int dochange = 1; 1916 1917 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) { 1918 dst[i] = src[i]; 1919 if (islower((unsigned char)dst[i])) 1920 dochange = 0; 1921 } 1922 dst[i] = '\0'; 1923 1924 if (dochange) { 1925 for (i = 0; dst[i] != '\0'; i++) 1926 if (isupper((unsigned char)dst[i])) 1927 dst[i] = tolower((unsigned char)dst[i]); 1928 } 1929 return dst; 1930 } 1931 1932 void 1933 setcr(int argc, char *argv[]) 1934 { 1935 1936 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1937 } 1938 1939 void 1940 setntrans(int argc, char *argv[]) 1941 { 1942 1943 if (argc == 0 || argc > 3) { 1944 UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]); 1945 code = -1; 1946 return; 1947 } 1948 if (argc == 1) { 1949 ntflag = 0; 1950 fputs("Ntrans off.\n", ttyout); 1951 code = ntflag; 1952 return; 1953 } 1954 ntflag++; 1955 code = ntflag; 1956 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1957 if (argc == 2) { 1958 ntout[0] = '\0'; 1959 return; 1960 } 1961 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1962 } 1963 1964 static const char * 1965 dotrans(char *dst, size_t dlen, const char *src) 1966 { 1967 const char *cp1; 1968 char *cp2 = dst; 1969 size_t i, ostop; 1970 1971 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 1972 continue; 1973 for (cp1 = src; *cp1; cp1++) { 1974 int found = 0; 1975 for (i = 0; *(ntin + i) && i < 16; i++) { 1976 if (*cp1 == *(ntin + i)) { 1977 found++; 1978 if (i < ostop) { 1979 *cp2++ = *(ntout + i); 1980 if (cp2 - dst >= (ptrdiff_t)(dlen - 1)) 1981 goto out; 1982 } 1983 break; 1984 } 1985 } 1986 if (!found) { 1987 *cp2++ = *cp1; 1988 } 1989 } 1990 out: 1991 *cp2 = '\0'; 1992 return dst; 1993 } 1994 1995 void 1996 setnmap(int argc, char *argv[]) 1997 { 1998 char *cp; 1999 2000 if (argc == 1) { 2001 mapflag = 0; 2002 fputs("Nmap off.\n", ttyout); 2003 code = mapflag; 2004 return; 2005 } 2006 if (argc == 0 || 2007 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { 2008 UPRINTF("usage: %s [mapin mapout]\n", argv[0]); 2009 code = -1; 2010 return; 2011 } 2012 mapflag = 1; 2013 code = 1; 2014 cp = strchr(altarg, ' '); 2015 if (proxy) { 2016 while(*++cp == ' ') 2017 continue; 2018 altarg = cp; 2019 cp = strchr(altarg, ' '); 2020 } 2021 *cp = '\0'; 2022 (void)strlcpy(mapin, altarg, MAXPATHLEN); 2023 while (*++cp == ' ') 2024 continue; 2025 (void)strlcpy(mapout, cp, MAXPATHLEN); 2026 } 2027 2028 static const char * 2029 domap(char *dst, size_t dlen, const char *src) 2030 { 2031 const char *cp1 = src; 2032 char *cp2 = mapin; 2033 const char *tp[9], *te[9]; 2034 int i, toks[9], toknum = 0, match = 1; 2035 2036 for (i=0; i < 9; ++i) { 2037 toks[i] = 0; 2038 } 2039 while (match && *cp1 && *cp2) { 2040 switch (*cp2) { 2041 case '\\': 2042 if (*++cp2 != *cp1) { 2043 match = 0; 2044 } 2045 break; 2046 case '$': 2047 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 2048 if (*cp1 != *(++cp2+1)) { 2049 toks[toknum = *cp2 - '1']++; 2050 tp[toknum] = cp1; 2051 while (*++cp1 && *(cp2+1) 2052 != *cp1); 2053 te[toknum] = cp1; 2054 } 2055 cp2++; 2056 break; 2057 } 2058 /* FALLTHROUGH */ 2059 default: 2060 if (*cp2 != *cp1) { 2061 match = 0; 2062 } 2063 break; 2064 } 2065 if (match && *cp1) { 2066 cp1++; 2067 } 2068 if (match && *cp2) { 2069 cp2++; 2070 } 2071 } 2072 if (!match && *cp1) /* last token mismatch */ 2073 { 2074 toks[toknum] = 0; 2075 } 2076 cp2 = dst; 2077 *cp2 = '\0'; 2078 cp1 = mapout; 2079 while (*cp1) { 2080 match = 0; 2081 switch (*cp1) { 2082 case '\\': 2083 if (*(cp1 + 1)) { 2084 *cp2++ = *++cp1; 2085 } 2086 break; 2087 case '[': 2088 LOOP: 2089 if (*++cp1 == '$' && 2090 isdigit((unsigned char)*(cp1+1))) { 2091 if (*++cp1 == '0') { 2092 const char *cp3 = src; 2093 2094 while (*cp3) { 2095 *cp2++ = *cp3++; 2096 } 2097 match = 1; 2098 } 2099 else if (toks[toknum = *cp1 - '1']) { 2100 const char *cp3 = tp[toknum]; 2101 2102 while (cp3 != te[toknum]) { 2103 *cp2++ = *cp3++; 2104 } 2105 match = 1; 2106 } 2107 } 2108 else { 2109 while (*cp1 && *cp1 != ',' && 2110 *cp1 != ']') { 2111 if (*cp1 == '\\') { 2112 cp1++; 2113 } 2114 else if (*cp1 == '$' && 2115 isdigit((unsigned char)*(cp1+1))) { 2116 if (*++cp1 == '0') { 2117 const char *cp3 = src; 2118 2119 while (*cp3) { 2120 *cp2++ = *cp3++; 2121 } 2122 } 2123 else if (toks[toknum = 2124 *cp1 - '1']) { 2125 const char *cp3=tp[toknum]; 2126 2127 while (cp3 != 2128 te[toknum]) { 2129 *cp2++ = *cp3++; 2130 } 2131 } 2132 } 2133 else if (*cp1) { 2134 *cp2++ = *cp1++; 2135 } 2136 } 2137 if (!*cp1) { 2138 fputs( 2139 "nmap: unbalanced brackets.\n", 2140 ttyout); 2141 return (src); 2142 } 2143 match = 1; 2144 cp1--; 2145 } 2146 if (match) { 2147 while (*++cp1 && *cp1 != ']') { 2148 if (*cp1 == '\\' && *(cp1 + 1)) { 2149 cp1++; 2150 } 2151 } 2152 if (!*cp1) { 2153 fputs( 2154 "nmap: unbalanced brackets.\n", 2155 ttyout); 2156 return (src); 2157 } 2158 break; 2159 } 2160 switch (*++cp1) { 2161 case ',': 2162 goto LOOP; 2163 case ']': 2164 break; 2165 default: 2166 cp1--; 2167 goto LOOP; 2168 } 2169 break; 2170 case '$': 2171 if (isdigit((unsigned char)*(cp1 + 1))) { 2172 if (*++cp1 == '0') { 2173 const char *cp3 = src; 2174 2175 while (*cp3) { 2176 *cp2++ = *cp3++; 2177 } 2178 } 2179 else if (toks[toknum = *cp1 - '1']) { 2180 const char *cp3 = tp[toknum]; 2181 2182 while (cp3 != te[toknum]) { 2183 *cp2++ = *cp3++; 2184 } 2185 } 2186 break; 2187 } 2188 /* intentional drop through */ 2189 default: 2190 *cp2++ = *cp1; 2191 break; 2192 } 2193 cp1++; 2194 } 2195 *cp2 = '\0'; 2196 return *dst ? dst : src; 2197 } 2198 2199 void 2200 setpassive(int argc, char *argv[]) 2201 { 2202 2203 if (argc == 1) { 2204 passivemode = !passivemode; 2205 activefallback = passivemode; 2206 } else if (argc != 2) { 2207 passiveusage: 2208 UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]); 2209 code = -1; 2210 return; 2211 } else if (strcasecmp(argv[1], "on") == 0) { 2212 passivemode = 1; 2213 activefallback = 0; 2214 } else if (strcasecmp(argv[1], "off") == 0) { 2215 passivemode = 0; 2216 activefallback = 0; 2217 } else if (strcasecmp(argv[1], "auto") == 0) { 2218 passivemode = 1; 2219 activefallback = 1; 2220 } else 2221 goto passiveusage; 2222 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", 2223 onoff(passivemode), onoff(activefallback)); 2224 code = passivemode; 2225 } 2226 2227 2228 void 2229 setepsv4(int argc, char *argv[]) 2230 { 2231 code = togglevar(argc, argv, &epsv4, 2232 verbose ? "EPSV/EPRT on IPv4" : NULL); 2233 epsv4bad = 0; 2234 } 2235 2236 void 2237 setepsv6(int argc, char *argv[]) 2238 { 2239 code = togglevar(argc, argv, &epsv6, 2240 verbose ? "EPSV/EPRT on IPv6" : NULL); 2241 epsv6bad = 0; 2242 } 2243 2244 void 2245 setepsv(int argc, char*argv[]) 2246 { 2247 setepsv4(argc,argv); 2248 setepsv6(argc,argv); 2249 } 2250 2251 void 2252 setsunique(int argc, char *argv[]) 2253 { 2254 2255 code = togglevar(argc, argv, &sunique, "Store unique"); 2256 } 2257 2258 void 2259 setrunique(int argc, char *argv[]) 2260 { 2261 2262 code = togglevar(argc, argv, &runique, "Receive unique"); 2263 } 2264 2265 int 2266 parserate(int argc, char *argv[], int cmdlineopt) 2267 { 2268 int dir, max, incr, showonly; 2269 sigfunc oldusr1, oldusr2; 2270 2271 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { 2272 usage: 2273 if (cmdlineopt) 2274 UPRINTF( 2275 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", 2276 argv[0]); 2277 else 2278 UPRINTF( 2279 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", 2280 argv[0]); 2281 return -1; 2282 } 2283 dir = max = incr = showonly = 0; 2284 #define RATE_GET 1 2285 #define RATE_PUT 2 2286 #define RATE_ALL (RATE_GET | RATE_PUT) 2287 2288 if (strcasecmp(argv[1], "all") == 0) 2289 dir = RATE_ALL; 2290 else if (strcasecmp(argv[1], "get") == 0) 2291 dir = RATE_GET; 2292 else if (strcasecmp(argv[1], "put") == 0) 2293 dir = RATE_PUT; 2294 else 2295 goto usage; 2296 2297 if (argc >= 3) { 2298 if ((max = strsuftoi(argv[2])) < 0) 2299 goto usage; 2300 } else 2301 showonly = 1; 2302 2303 if (argc == 4) { 2304 if ((incr = strsuftoi(argv[3])) <= 0) 2305 goto usage; 2306 } else 2307 incr = DEFAULTINCR; 2308 2309 oldusr1 = xsignal(SIGUSR1, SIG_IGN); 2310 oldusr2 = xsignal(SIGUSR2, SIG_IGN); 2311 if (dir & RATE_GET) { 2312 if (!showonly) { 2313 rate_get = max; 2314 rate_get_incr = incr; 2315 } 2316 if (!cmdlineopt || verbose) 2317 fprintf(ttyout, 2318 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", 2319 onoff(rate_get), rate_get, rate_get_incr); 2320 } 2321 if (dir & RATE_PUT) { 2322 if (!showonly) { 2323 rate_put = max; 2324 rate_put_incr = incr; 2325 } 2326 if (!cmdlineopt || verbose) 2327 fprintf(ttyout, 2328 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", 2329 onoff(rate_put), rate_put, rate_put_incr); 2330 } 2331 (void)xsignal(SIGUSR1, oldusr1); 2332 (void)xsignal(SIGUSR2, oldusr2); 2333 return 0; 2334 } 2335 2336 void 2337 setrate(int argc, char *argv[]) 2338 { 2339 2340 code = parserate(argc, argv, 0); 2341 } 2342 2343 /* change directory to parent directory */ 2344 void 2345 cdup(int argc, char *argv[]) 2346 { 2347 int r; 2348 2349 if (argc == 0) { 2350 UPRINTF("usage: %s\n", argv[0]); 2351 code = -1; 2352 return; 2353 } 2354 r = command("CDUP"); 2355 if (r == ERROR && code == 500) { 2356 if (verbose) 2357 fputs("CDUP command not recognized, trying XCUP.\n", 2358 ttyout); 2359 r = command("XCUP"); 2360 } 2361 if (r == COMPLETE) { 2362 dirchange = 1; 2363 updateremotecwd(); 2364 } 2365 } 2366 2367 /* 2368 * Restart transfer at specific point 2369 */ 2370 void 2371 restart(int argc, char *argv[]) 2372 { 2373 2374 if (argc == 0 || argc > 2) { 2375 UPRINTF("usage: %s [restart-point]\n", argv[0]); 2376 code = -1; 2377 return; 2378 } 2379 if (! features[FEAT_REST_STREAM]) { 2380 fprintf(ttyout, 2381 "Restart is not supported by the remote server.\n"); 2382 return; 2383 } 2384 if (argc == 2) { 2385 off_t rp; 2386 char *ep; 2387 2388 rp = STRTOLL(argv[1], &ep, 10); 2389 if (rp < 0 || *ep != '\0') 2390 fprintf(ttyout, "restart: Invalid offset `%s'\n", 2391 argv[1]); 2392 else 2393 restart_point = rp; 2394 } 2395 if (restart_point == 0) 2396 fputs("No restart point defined.\n", ttyout); 2397 else 2398 fprintf(ttyout, 2399 "Restarting at " LLF " for next get, put or append\n", 2400 (LLT)restart_point); 2401 } 2402 2403 /* 2404 * Show remote system type 2405 */ 2406 void 2407 syst(int argc, char *argv[]) 2408 { 2409 int oldverbose = verbose; 2410 2411 if (argc == 0) { 2412 UPRINTF("usage: %s\n", argv[0]); 2413 code = -1; 2414 return; 2415 } 2416 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2417 (void)command("SYST"); 2418 verbose = oldverbose; 2419 } 2420 2421 void 2422 macdef(int argc, char *argv[]) 2423 { 2424 char *tmp; 2425 int c; 2426 2427 if (argc == 0) 2428 goto usage; 2429 if (macnum == 16) { 2430 fputs("Limit of 16 macros have already been defined.\n", 2431 ttyout); 2432 code = -1; 2433 return; 2434 } 2435 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { 2436 usage: 2437 UPRINTF("usage: %s macro_name\n", argv[0]); 2438 code = -1; 2439 return; 2440 } 2441 if (interactive) 2442 fputs( 2443 "Enter macro line by line, terminating it with a null line.\n", 2444 ttyout); 2445 (void)strlcpy(macros[macnum].mac_name, argv[1], 2446 sizeof(macros[macnum].mac_name)); 2447 if (macnum == 0) 2448 macros[macnum].mac_start = macbuf; 2449 else 2450 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2451 tmp = macros[macnum].mac_start; 2452 while (tmp != macbuf+4096) { 2453 if ((c = getchar()) == EOF) { 2454 fputs("macdef: end of file encountered.\n", ttyout); 2455 code = -1; 2456 return; 2457 } 2458 if ((*tmp = c) == '\n') { 2459 if (tmp == macros[macnum].mac_start) { 2460 macros[macnum++].mac_end = tmp; 2461 code = 0; 2462 return; 2463 } 2464 if (*(tmp-1) == '\0') { 2465 macros[macnum++].mac_end = tmp - 1; 2466 code = 0; 2467 return; 2468 } 2469 *tmp = '\0'; 2470 } 2471 tmp++; 2472 } 2473 while (1) { 2474 while ((c = getchar()) != '\n' && c != EOF) 2475 /* LOOP */; 2476 if (c == EOF || getchar() == '\n') { 2477 fputs("Macro not defined - 4K buffer exceeded.\n", 2478 ttyout); 2479 code = -1; 2480 return; 2481 } 2482 } 2483 } 2484 2485 /* 2486 * Get size of file on remote machine 2487 */ 2488 void 2489 sizecmd(int argc, char *argv[]) 2490 { 2491 off_t size; 2492 2493 if (argc == 0 || argc > 2 || 2494 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2495 UPRINTF("usage: %s remote-file\n", argv[0]); 2496 code = -1; 2497 return; 2498 } 2499 size = remotesize(argv[1], 1); 2500 if (size != -1) 2501 fprintf(ttyout, 2502 "%s\t" LLF "\n", argv[1], (LLT)size); 2503 code = (size > 0); 2504 } 2505 2506 /* 2507 * Get last modification time of file on remote machine 2508 */ 2509 void 2510 modtime(int argc, char *argv[]) 2511 { 2512 time_t mtime; 2513 2514 if (argc == 0 || argc > 2 || 2515 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2516 UPRINTF("usage: %s remote-file\n", argv[0]); 2517 code = -1; 2518 return; 2519 } 2520 mtime = remotemodtime(argv[1], 1); 2521 if (mtime != -1) 2522 fprintf(ttyout, "%s\t%s", argv[1], 2523 rfc2822time(localtime(&mtime))); 2524 code = (mtime > 0); 2525 } 2526 2527 /* 2528 * Show status on remote machine 2529 */ 2530 void 2531 rmtstatus(int argc, char *argv[]) 2532 { 2533 2534 if (argc == 0) { 2535 UPRINTF("usage: %s [remote-file]\n", argv[0]); 2536 code = -1; 2537 return; 2538 } 2539 COMMAND_1ARG(argc, argv, "STAT"); 2540 } 2541 2542 /* 2543 * Get file if modtime is more recent than current file 2544 */ 2545 void 2546 newer(int argc, char *argv[]) 2547 { 2548 2549 if (getit(argc, argv, -1, "w")) 2550 fprintf(ttyout, 2551 "Local file \"%s\" is newer than remote file \"%s\".\n", 2552 argv[2], argv[1]); 2553 } 2554 2555 /* 2556 * Display one local file through $PAGER. 2557 */ 2558 void 2559 lpage(int argc, char *argv[]) 2560 { 2561 size_t len; 2562 const char *p; 2563 char *pager, *locfile; 2564 2565 if (argc == 0 || argc > 2 || 2566 (argc == 1 && !another(&argc, &argv, "local-file"))) { 2567 UPRINTF("usage: %s local-file\n", argv[0]); 2568 code = -1; 2569 return; 2570 } 2571 if ((locfile = globulize(argv[1])) == NULL) { 2572 code = -1; 2573 return; 2574 } 2575 p = getoptionvalue("pager"); 2576 if (EMPTYSTRING(p)) 2577 p = DEFAULTPAGER; 2578 len = strlen(p) + strlen(locfile) + 2; 2579 pager = ftp_malloc(len); 2580 (void)strlcpy(pager, p, len); 2581 (void)strlcat(pager, " ", len); 2582 (void)strlcat(pager, locfile, len); 2583 system(pager); 2584 code = 0; 2585 (void)free(pager); 2586 (void)free(locfile); 2587 } 2588 2589 /* 2590 * Display one remote file through $PAGER. 2591 */ 2592 void 2593 page(int argc, char *argv[]) 2594 { 2595 int ohash, orestart_point, overbose; 2596 size_t len; 2597 const char *p; 2598 char *pager; 2599 2600 if (argc == 0 || argc > 2 || 2601 (argc == 1 && !another(&argc, &argv, "remote-file"))) { 2602 UPRINTF("usage: %s remote-file\n", argv[0]); 2603 code = -1; 2604 return; 2605 } 2606 p = getoptionvalue("pager"); 2607 if (EMPTYSTRING(p)) 2608 p = DEFAULTPAGER; 2609 len = strlen(p) + 2; 2610 pager = ftp_malloc(len); 2611 pager[0] = '|'; 2612 (void)strlcpy(pager + 1, p, len - 1); 2613 2614 ohash = hash; 2615 orestart_point = restart_point; 2616 overbose = verbose; 2617 hash = restart_point = verbose = 0; 2618 recvrequest("RETR", pager, argv[1], "r+", 1, 0); 2619 hash = ohash; 2620 restart_point = orestart_point; 2621 verbose = overbose; 2622 (void)free(pager); 2623 } 2624 2625 /* 2626 * Set the socket send or receive buffer size. 2627 */ 2628 void 2629 setxferbuf(int argc, char *argv[]) 2630 { 2631 int size, dir; 2632 2633 if (argc != 2) { 2634 usage: 2635 UPRINTF("usage: %s size\n", argv[0]); 2636 code = -1; 2637 return; 2638 } 2639 if (strcasecmp(argv[0], "sndbuf") == 0) 2640 dir = RATE_PUT; 2641 else if (strcasecmp(argv[0], "rcvbuf") == 0) 2642 dir = RATE_GET; 2643 else if (strcasecmp(argv[0], "xferbuf") == 0) 2644 dir = RATE_ALL; 2645 else 2646 goto usage; 2647 2648 if ((size = strsuftoi(argv[1])) == -1) 2649 goto usage; 2650 2651 if (size == 0) { 2652 fprintf(ttyout, "%s: size must be positive.\n", argv[0]); 2653 goto usage; 2654 } 2655 2656 if (dir & RATE_PUT) 2657 sndbuf_size = size; 2658 if (dir & RATE_GET) 2659 rcvbuf_size = size; 2660 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n", 2661 sndbuf_size, rcvbuf_size); 2662 code = 0; 2663 } 2664 2665 /* 2666 * Set or display options (defaults are provided by various env vars) 2667 */ 2668 void 2669 setoption(int argc, char *argv[]) 2670 { 2671 struct option *o; 2672 2673 code = -1; 2674 if (argc == 0 || (argc != 1 && argc != 3)) { 2675 UPRINTF("usage: %s [option value]\n", argv[0]); 2676 return; 2677 } 2678 2679 #define OPTIONINDENT ((int) sizeof("http_proxy")) 2680 if (argc == 1) { 2681 for (o = optiontab; o->name != NULL; o++) { 2682 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, 2683 o->name, o->value ? o->value : ""); 2684 } 2685 } else { 2686 set_option(argv[1], argv[2], 1); 2687 } 2688 code = 0; 2689 } 2690 2691 void 2692 set_option(const char * option, const char * value, int doverbose) 2693 { 2694 struct option *o; 2695 2696 o = getoption(option); 2697 if (o == NULL) { 2698 fprintf(ttyout, "No such option `%s'.\n", option); 2699 return; 2700 } 2701 FREEPTR(o->value); 2702 o->value = ftp_strdup(value); 2703 if (verbose && doverbose) 2704 fprintf(ttyout, "Setting `%s' to `%s'.\n", 2705 o->name, o->value); 2706 } 2707 2708 /* 2709 * Unset an option 2710 */ 2711 void 2712 unsetoption(int argc, char *argv[]) 2713 { 2714 struct option *o; 2715 2716 code = -1; 2717 if (argc == 0 || argc != 2) { 2718 UPRINTF("usage: %s option\n", argv[0]); 2719 return; 2720 } 2721 2722 o = getoption(argv[1]); 2723 if (o == NULL) { 2724 fprintf(ttyout, "No such option `%s'.\n", argv[1]); 2725 return; 2726 } 2727 FREEPTR(o->value); 2728 fprintf(ttyout, "Unsetting `%s'.\n", o->name); 2729 code = 0; 2730 } 2731 2732 /* 2733 * Display features supported by the remote host. 2734 */ 2735 void 2736 feat(int argc, char *argv[]) 2737 { 2738 int oldverbose = verbose; 2739 2740 if (argc == 0) { 2741 UPRINTF("usage: %s\n", argv[0]); 2742 code = -1; 2743 return; 2744 } 2745 if (! features[FEAT_FEAT]) { 2746 fprintf(ttyout, 2747 "FEAT is not supported by the remote server.\n"); 2748 return; 2749 } 2750 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2751 (void)command("FEAT"); 2752 verbose = oldverbose; 2753 } 2754 2755 void 2756 mlst(int argc, char *argv[]) 2757 { 2758 int oldverbose = verbose; 2759 2760 if (argc < 1 || argc > 2) { 2761 UPRINTF("usage: %s [remote-path]\n", argv[0]); 2762 code = -1; 2763 return; 2764 } 2765 if (! features[FEAT_MLST]) { 2766 fprintf(ttyout, 2767 "MLST is not supported by the remote server.\n"); 2768 return; 2769 } 2770 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2771 COMMAND_1ARG(argc, argv, "MLST"); 2772 verbose = oldverbose; 2773 } 2774 2775 void 2776 opts(int argc, char *argv[]) 2777 { 2778 int oldverbose = verbose; 2779 2780 if (argc < 2 || argc > 3) { 2781 UPRINTF("usage: %s command [options]\n", argv[0]); 2782 code = -1; 2783 return; 2784 } 2785 if (! features[FEAT_FEAT]) { 2786 fprintf(ttyout, 2787 "OPTS is not supported by the remote server.\n"); 2788 return; 2789 } 2790 verbose = 1; /* If we aren't verbose, this doesn't do anything! */ 2791 if (argc == 2) 2792 command("OPTS %s", argv[1]); 2793 else 2794 command("OPTS %s %s", argv[1], argv[2]); 2795 verbose = oldverbose; 2796 } 2797