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