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