1 /* $OpenBSD: cmds.c,v 1.84 2019/11/18 04:37:35 deraadt Exp $ */ 2 /* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */ 3 4 /* 5 * Copyright (C) 1997 and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1985, 1989, 1993, 1994 35 * The Regents of the University of California. All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 #ifndef SMALL 63 64 /* 65 * FTP User Program -- Command Routines. 66 */ 67 #include <sys/types.h> 68 #include <sys/socket.h> 69 #include <sys/stat.h> 70 #include <sys/wait.h> 71 #include <arpa/ftp.h> 72 73 #include <ctype.h> 74 #include <err.h> 75 #include <fnmatch.h> 76 #include <glob.h> 77 #include <netdb.h> 78 #include <stdio.h> 79 #include <stdlib.h> 80 #include <string.h> 81 #include <unistd.h> 82 #include <errno.h> 83 84 #include "ftp_var.h" 85 #include "pathnames.h" 86 #include "cmds.h" 87 88 /* 89 * Set ascii transfer type. 90 */ 91 /*ARGSUSED*/ 92 void 93 setascii(int argc, char *argv[]) 94 { 95 96 stype[1] = "ascii"; 97 settype(2, stype); 98 } 99 100 /* 101 * Set file transfer mode. 102 */ 103 /*ARGSUSED*/ 104 void 105 setftmode(int argc, char *argv[]) 106 { 107 108 fprintf(ttyout, "We only support %s mode, sorry.\n", modename); 109 code = -1; 110 } 111 112 /* 113 * Set file transfer format. 114 */ 115 /*ARGSUSED*/ 116 void 117 setform(int argc, char *argv[]) 118 { 119 120 fprintf(ttyout, "We only support %s format, sorry.\n", formname); 121 code = -1; 122 } 123 124 /* 125 * Set file transfer structure. 126 */ 127 /*ARGSUSED*/ 128 void 129 setstruct(int argc, char *argv[]) 130 { 131 132 fprintf(ttyout, "We only support %s structure, sorry.\n", structname); 133 code = -1; 134 } 135 136 void 137 reput(int argc, char *argv[]) 138 { 139 140 (void)putit(argc, argv, 1); 141 } 142 143 void 144 put(int argc, char *argv[]) 145 { 146 147 (void)putit(argc, argv, 0); 148 } 149 150 /* 151 * Send a single file. 152 */ 153 void 154 putit(int argc, char *argv[], int restartit) 155 { 156 char *cmd; 157 int loc = 0; 158 char *oldargv1, *oldargv2; 159 160 if (argc == 2) { 161 argc++; 162 argv[2] = argv[1]; 163 loc++; 164 } 165 if (argc < 2 && !another(&argc, &argv, "local-file")) 166 goto usage; 167 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { 168 usage: 169 fprintf(ttyout, "usage: %s local-file [remote-file]\n", 170 argv[0]); 171 code = -1; 172 return; 173 } 174 oldargv1 = argv[1]; 175 oldargv2 = argv[2]; 176 if (!globulize(&argv[1])) { 177 code = -1; 178 return; 179 } 180 /* 181 * If "globulize" modifies argv[1], and argv[2] is a copy of 182 * the old argv[1], make it a copy of the new argv[1]. 183 */ 184 if (argv[1] != oldargv1 && argv[2] == oldargv1) { 185 argv[2] = argv[1]; 186 } 187 if (restartit == 1) { 188 if (curtype != type) 189 changetype(type, 0); 190 restart_point = remotesize(argv[2], 1); 191 if (restart_point < 0) { 192 restart_point = 0; 193 code = -1; 194 return; 195 } 196 } 197 if (strcmp(argv[0], "append") == 0) { 198 restartit = 1; 199 } 200 cmd = restartit ? "APPE" : ((sunique) ? "STOU" : "STOR"); 201 if (loc && ntflag) { 202 argv[2] = dotrans(argv[2]); 203 } 204 if (loc && mapflag) { 205 argv[2] = domap(argv[2]); 206 } 207 sendrequest(cmd, argv[1], argv[2], 208 argv[1] != oldargv1 || argv[2] != oldargv2); 209 restart_point = 0; 210 if (oldargv1 != argv[1]) /* free up after globulize() */ 211 free(argv[1]); 212 } 213 214 /* 215 * Send multiple files. 216 */ 217 void 218 mput(int argc, char *argv[]) 219 { 220 extern int optind, optreset; 221 int ch, i, restartit = 0; 222 sig_t oldintr; 223 char *cmd, *tp, *xargv[] = { argv[0], NULL, NULL }; 224 const char *errstr; 225 static int depth = 0, max_depth = 0; 226 227 optind = optreset = 1; 228 229 if (depth) 230 depth++; 231 232 while ((ch = getopt(argc, argv, "cd:r")) != -1) { 233 switch(ch) { 234 case 'c': 235 restartit = 1; 236 break; 237 case 'd': 238 max_depth = strtonum(optarg, 0, INT_MAX, &errstr); 239 if (errstr != NULL) { 240 fprintf(ttyout, "bad depth value, %s: %s\n", 241 errstr, optarg); 242 code = -1; 243 return; 244 } 245 break; 246 case 'r': 247 depth = 1; 248 break; 249 default: 250 goto usage; 251 } 252 } 253 254 if (argc - optind < 1 && !another(&argc, &argv, "local-files")) { 255 usage: 256 fprintf(ttyout, "usage: %s [-cr] [-d depth] local-files\n", 257 argv[0]); 258 code = -1; 259 return; 260 } 261 262 argv[optind - 1] = argv[0]; 263 argc -= optind - 1; 264 argv += optind - 1; 265 266 mname = argv[0]; 267 mflag = 1; 268 269 oldintr = signal(SIGINT, mabort); 270 (void)setjmp(jabort); 271 if (proxy) { 272 char *cp, *tp2, tmpbuf[PATH_MAX]; 273 274 while ((cp = remglob(argv, 0, NULL)) != NULL) { 275 if (*cp == '\0') { 276 mflag = 0; 277 continue; 278 } 279 if (mflag && confirm(argv[0], cp)) { 280 tp = cp; 281 if (mcase) { 282 while (*tp && !islower((unsigned char)*tp)) { 283 tp++; 284 } 285 if (!*tp) { 286 tp = cp; 287 tp2 = tmpbuf; 288 while ((*tp2 = *tp) != '\0') { 289 if (isupper((unsigned char)*tp2)) { 290 *tp2 = 291 tolower((unsigned char)*tp2); 292 } 293 tp++; 294 tp2++; 295 } 296 } 297 tp = tmpbuf; 298 } 299 if (ntflag) { 300 tp = dotrans(tp); 301 } 302 if (mapflag) { 303 tp = domap(tp); 304 } 305 if (restartit == 1) { 306 off_t ret; 307 308 if (curtype != type) 309 changetype(type, 0); 310 ret = remotesize(tp, 0); 311 restart_point = (ret < 0) ? 0 : ret; 312 } 313 cmd = restartit ? "APPE" : ((sunique) ? 314 "STOU" : "STOR"); 315 sendrequest(cmd, cp, tp, 316 cp != tp || !interactive); 317 restart_point = 0; 318 if (!mflag && fromatty) { 319 if (confirm(argv[0], NULL)) 320 mflag = 1; 321 } 322 } 323 } 324 (void)signal(SIGINT, oldintr); 325 mflag = 0; 326 return; 327 } 328 329 for (i = 1; i < argc; i++) { 330 char **cpp; 331 glob_t gl; 332 int flags; 333 334 /* Copy files without word expansion */ 335 if (!doglob) { 336 if (mflag && confirm(argv[0], argv[i])) { 337 tp = (ntflag) ? dotrans(argv[i]) : argv[i]; 338 tp = (mapflag) ? domap(tp) : tp; 339 if (restartit == 1) { 340 off_t ret; 341 342 if (curtype != type) 343 changetype(type, 0); 344 ret = remotesize(tp, 0); 345 restart_point = (ret < 0) ? 0 : ret; 346 } 347 cmd = restartit ? "APPE" : ((sunique) ? 348 "STOU" : "STOR"); 349 sendrequest(cmd, argv[i], tp, 350 tp != argv[i] || !interactive); 351 restart_point = 0; 352 if (!mflag && fromatty) { 353 if (confirm(argv[0], NULL)) 354 mflag = 1; 355 } 356 } 357 continue; 358 } 359 360 /* expanding file names */ 361 memset(&gl, 0, sizeof(gl)); 362 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 363 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { 364 warnx("%s: not found", argv[i]); 365 globfree(&gl); 366 continue; 367 } 368 369 /* traverse all expanded file names */ 370 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) { 371 struct stat filestat; 372 373 if (!mflag) 374 continue; 375 if (stat(*cpp, &filestat) != 0) { 376 warn("local: %s", *cpp); 377 continue; 378 } 379 if (S_ISDIR(filestat.st_mode) && depth == max_depth) 380 continue; 381 if (!confirm(argv[0], *cpp)) 382 continue; 383 384 /* 385 * If file is a directory then create a new one 386 * at the remote machine. 387 */ 388 if (S_ISDIR(filestat.st_mode)) { 389 xargv[1] = *cpp; 390 makedir(2, xargv); 391 cd(2, xargv); 392 if (dirchange != 1) { 393 warnx("remote: %s", *cpp); 394 continue; 395 } 396 397 if (chdir(*cpp) != 0) { 398 warn("local: %s", *cpp); 399 goto out; 400 } 401 402 /* Copy the whole directory recursively. */ 403 xargv[1] = "*"; 404 mput(2, xargv); 405 406 if (chdir("..") != 0) { 407 mflag = 0; 408 warn("local: %s", *cpp); 409 goto out; 410 } 411 412 out: 413 xargv[1] = ".."; 414 cd(2, xargv); 415 if (dirchange != 1) { 416 warnx("remote: %s", *cpp); 417 mflag = 0; 418 } 419 continue; 420 } 421 422 tp = (ntflag) ? dotrans(*cpp) : *cpp; 423 tp = (mapflag) ? domap(tp) : tp; 424 if (restartit == 1) { 425 off_t ret; 426 427 if (curtype != type) 428 changetype(type, 0); 429 ret = remotesize(tp, 0); 430 restart_point = (ret < 0) ? 0 : ret; 431 } 432 cmd = restartit ? "APPE" : ((sunique) ? 433 "STOU" : "STOR"); 434 sendrequest(cmd, *cpp, tp, 435 *cpp != tp || !interactive); 436 restart_point = 0; 437 if (!mflag && fromatty) { 438 if (confirm(argv[0], NULL)) 439 mflag = 1; 440 } 441 } 442 globfree(&gl); 443 } 444 445 (void)signal(SIGINT, oldintr); 446 447 if (depth) 448 depth--; 449 if (depth == 0 || mflag == 0) 450 depth = max_depth = mflag = 0; 451 } 452 453 void 454 reget(int argc, char *argv[]) 455 { 456 457 (void)getit(argc, argv, 1, "a+w"); 458 } 459 460 char * 461 onoff(int bool) 462 { 463 464 return (bool ? "on" : "off"); 465 } 466 467 /* 468 * Show status. 469 */ 470 /*ARGSUSED*/ 471 void 472 status(int argc, char *argv[]) 473 { 474 int i; 475 476 if (connected) 477 fprintf(ttyout, "Connected %sto %s.\n", 478 connected == -1 ? "and logged in" : "", hostname); 479 else 480 fputs("Not connected.\n", ttyout); 481 if (!proxy) { 482 pswitch(1); 483 if (connected) { 484 fprintf(ttyout, "Connected for proxy commands to %s.\n", 485 hostname); 486 } 487 else { 488 fputs("No proxy connection.\n", ttyout); 489 } 490 pswitch(0); 491 } 492 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), 493 *gateserver ? gateserver : "(none)", gateport); 494 fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode)); 495 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", 496 modename, typename, formname, structname); 497 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", 498 onoff(verbose), onoff(bell), onoff(interactive), 499 onoff(doglob)); 500 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", onoff(sunique), 501 onoff(runique)); 502 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); 503 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag)); 504 if (ntflag) { 505 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); 506 } 507 else { 508 fputs("Ntrans: off.\n", ttyout); 509 } 510 if (mapflag) { 511 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); 512 } 513 else { 514 fputs("Nmap: off.\n", ttyout); 515 } 516 fprintf(ttyout, "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", 517 onoff(hash), mark, onoff(progress)); 518 fprintf(ttyout, "Use of PORT/LPRT cmds: %s.\n", onoff(sendport)); 519 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), 520 epsv4bad ? " (disabled for this connection)" : ""); 521 fprintf(ttyout, "Command line editing: %s.\n", onoff(editing)); 522 if (macnum > 0) { 523 fputs("Macros:\n", ttyout); 524 for (i=0; i<macnum; i++) { 525 fprintf(ttyout, "\t%s\n", macros[i].mac_name); 526 } 527 } 528 code = 0; 529 } 530 531 /* 532 * Toggle a variable 533 */ 534 int 535 togglevar(int argc, char *argv[], int *var, const char *mesg) 536 { 537 if (argc < 2) { 538 *var = !*var; 539 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { 540 *var = 1; 541 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { 542 *var = 0; 543 } else { 544 fprintf(ttyout, "usage: %s [on | off]\n", argv[0]); 545 return (-1); 546 } 547 if (mesg) 548 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); 549 return (*var); 550 } 551 552 /* 553 * Set beep on cmd completed mode. 554 */ 555 /*ARGSUSED*/ 556 void 557 setbell(int argc, char *argv[]) 558 { 559 560 code = togglevar(argc, argv, &bell, "Bell mode"); 561 } 562 563 /* 564 * Set command line editing 565 */ 566 /*ARGSUSED*/ 567 void 568 setedit(int argc, char *argv[]) 569 { 570 571 code = togglevar(argc, argv, &editing, "Editing mode"); 572 controlediting(); 573 } 574 575 /* 576 * Toggle use of IPv4 EPSV/EPRT 577 */ 578 /*ARGSUSED*/ 579 void 580 setepsv4(int argc, char *argv[]) 581 { 582 583 code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4"); 584 epsv4bad = 0; 585 } 586 587 /* 588 * Turn on packet tracing. 589 */ 590 /*ARGSUSED*/ 591 void 592 settrace(int argc, char *argv[]) 593 { 594 595 code = togglevar(argc, argv, &trace, "Packet tracing"); 596 } 597 598 /* 599 * Toggle hash mark printing during transfers, or set hash mark bytecount. 600 */ 601 /*ARGSUSED*/ 602 void 603 sethash(int argc, char *argv[]) 604 { 605 if (argc == 1) 606 hash = !hash; 607 else if (argc != 2) { 608 fprintf(ttyout, "usage: %s [on | off | size]\n", argv[0]); 609 code = -1; 610 return; 611 } else if (strcasecmp(argv[1], "on") == 0) 612 hash = 1; 613 else if (strcasecmp(argv[1], "off") == 0) 614 hash = 0; 615 else { 616 int nmark; 617 const char *errstr; 618 619 nmark = strtonum(argv[1], 1, INT_MAX, &errstr); 620 if (errstr) { 621 fprintf(ttyout, "bytecount value is %s: %s\n", 622 errstr, argv[1]); 623 code = -1; 624 return; 625 } 626 mark = nmark; 627 hash = 1; 628 } 629 fprintf(ttyout, "Hash mark printing %s", onoff(hash)); 630 if (hash) 631 fprintf(ttyout, " (%d bytes/hash mark)", mark); 632 fputs(".\n", ttyout); 633 code = hash; 634 } 635 636 /* 637 * Turn on printing of server echo's. 638 */ 639 /*ARGSUSED*/ 640 void 641 setverbose(int argc, char *argv[]) 642 { 643 644 code = togglevar(argc, argv, &verbose, "Verbose mode"); 645 } 646 647 /* 648 * Toggle PORT/LPRT cmd use before each data connection. 649 */ 650 /*ARGSUSED*/ 651 void 652 setport(int argc, char *argv[]) 653 { 654 655 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); 656 } 657 658 /* 659 * Toggle transfer progress bar. 660 */ 661 /*ARGSUSED*/ 662 void 663 setprogress(int argc, char *argv[]) 664 { 665 666 code = togglevar(argc, argv, &progress, "Progress bar"); 667 } 668 669 /* 670 * Turn on interactive prompting during mget, mput, and mdelete. 671 */ 672 /*ARGSUSED*/ 673 void 674 setprompt(int argc, char *argv[]) 675 { 676 677 code = togglevar(argc, argv, &interactive, "Interactive mode"); 678 } 679 680 /* 681 * Toggle gate-ftp mode, or set gate-ftp server 682 */ 683 /*ARGSUSED*/ 684 void 685 setgate(int argc, char *argv[]) 686 { 687 static char gsbuf[HOST_NAME_MAX+1]; 688 689 if (argc > 3) { 690 fprintf(ttyout, "usage: %s [on | off | host [port]]\n", 691 argv[0]); 692 code = -1; 693 return; 694 } else if (argc < 2) { 695 gatemode = !gatemode; 696 } else { 697 if (argc == 2 && strcasecmp(argv[1], "on") == 0) 698 gatemode = 1; 699 else if (argc == 2 && strcasecmp(argv[1], "off") == 0) 700 gatemode = 0; 701 else { 702 if (argc == 3) { 703 gateport = strdup(argv[2]); 704 if (gateport == NULL) 705 err(1, NULL); 706 } 707 strlcpy(gsbuf, argv[1], sizeof(gsbuf)); 708 gateserver = gsbuf; 709 gatemode = 1; 710 } 711 } 712 if (gatemode && (gateserver == NULL || *gateserver == '\0')) { 713 fprintf(ttyout, 714 "Disabling gate-ftp mode - no gate-ftp server defined.\n"); 715 gatemode = 0; 716 } else { 717 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", 718 onoff(gatemode), 719 *gateserver ? gateserver : "(none)", gateport); 720 } 721 code = gatemode; 722 } 723 724 /* 725 * Toggle metacharacter interpretation on local file names. 726 */ 727 /*ARGSUSED*/ 728 void 729 setglob(int argc, char *argv[]) 730 { 731 732 code = togglevar(argc, argv, &doglob, "Globbing"); 733 } 734 735 /* 736 * Toggle preserving modification times on retrieved files. 737 */ 738 /*ARGSUSED*/ 739 void 740 setpreserve(int argc, char *argv[]) 741 { 742 743 code = togglevar(argc, argv, &preserve, "Preserve modification times"); 744 } 745 746 /* 747 * Set debugging mode on/off and/or set level of debugging. 748 */ 749 /*ARGSUSED*/ 750 void 751 setdebug(int argc, char *argv[]) 752 { 753 if (argc > 2) { 754 fprintf(ttyout, "usage: %s [on | off | debuglevel]\n", argv[0]); 755 code = -1; 756 return; 757 } else if (argc == 2) { 758 if (strcasecmp(argv[1], "on") == 0) 759 debug = 1; 760 else if (strcasecmp(argv[1], "off") == 0) 761 debug = 0; 762 else { 763 const char *errstr; 764 int val; 765 766 val = strtonum(argv[1], 0, INT_MAX, &errstr); 767 if (errstr) { 768 fprintf(ttyout, "debugging value is %s: %s\n", 769 errstr, argv[1]); 770 code = -1; 771 return; 772 } 773 debug = val; 774 } 775 } else 776 debug = !debug; 777 if (debug) 778 options |= SO_DEBUG; 779 else 780 options &= ~SO_DEBUG; 781 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug); 782 code = debug > 0; 783 } 784 785 /* 786 * Set current working directory on local machine. 787 */ 788 void 789 lcd(int argc, char *argv[]) 790 { 791 char buf[PATH_MAX]; 792 char *oldargv1; 793 794 if (argc < 2) 795 argc++, argv[1] = home; 796 if (argc != 2) { 797 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]); 798 code = -1; 799 return; 800 } 801 oldargv1 = argv[1]; 802 if (!globulize(&argv[1])) { 803 code = -1; 804 return; 805 } 806 if (chdir(argv[1]) == -1) { 807 warn("local: %s", argv[1]); 808 code = -1; 809 } else { 810 if (getcwd(buf, sizeof(buf)) != NULL) 811 fprintf(ttyout, "Local directory now %s\n", buf); 812 else 813 warn("getcwd: %s", argv[1]); 814 code = 0; 815 } 816 if (oldargv1 != argv[1]) /* free up after globulize() */ 817 free(argv[1]); 818 } 819 820 /* 821 * Delete a single file. 822 */ 823 void 824 deletecmd(int argc, char *argv[]) 825 { 826 827 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) { 828 fprintf(ttyout, "usage: %s remote-file\n", argv[0]); 829 code = -1; 830 return; 831 } 832 (void)command("DELE %s", argv[1]); 833 } 834 835 /* 836 * Delete multiple files. 837 */ 838 void 839 mdelete(int argc, char *argv[]) 840 { 841 sig_t oldintr; 842 char *cp; 843 844 if (argc < 2 && !another(&argc, &argv, "remote-files")) { 845 fprintf(ttyout, "usage: %s remote-files\n", argv[0]); 846 code = -1; 847 return; 848 } 849 mname = argv[0]; 850 mflag = 1; 851 oldintr = signal(SIGINT, mabort); 852 (void)setjmp(jabort); 853 while ((cp = remglob(argv, 0, NULL)) != NULL) { 854 if (*cp == '\0') { 855 mflag = 0; 856 continue; 857 } 858 if (mflag && confirm(argv[0], cp)) { 859 (void)command("DELE %s", cp); 860 if (!mflag && fromatty) { 861 if (confirm(argv[0], NULL)) 862 mflag = 1; 863 } 864 } 865 } 866 (void)signal(SIGINT, oldintr); 867 mflag = 0; 868 } 869 870 /* 871 * Rename a remote file. 872 */ 873 void 874 renamefile(int argc, char *argv[]) 875 { 876 877 if (argc < 2 && !another(&argc, &argv, "from-name")) 878 goto usage; 879 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { 880 usage: 881 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]); 882 code = -1; 883 return; 884 } 885 if (command("RNFR %s", argv[1]) == CONTINUE) 886 (void)command("RNTO %s", argv[2]); 887 } 888 889 /* 890 * Get a directory listing of remote files. 891 */ 892 void 893 ls(int argc, char *argv[]) 894 { 895 const char *cmd; 896 char *oldargv2, *globargv2; 897 898 if (argc < 2) 899 argc++, argv[1] = NULL; 900 if (argc < 3) 901 argc++, argv[2] = "-"; 902 if (argc > 3) { 903 fprintf(ttyout, "usage: %s [remote-directory [local-file]]\n", 904 argv[0]); 905 code = -1; 906 return; 907 } 908 cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST"; 909 oldargv2 = argv[2]; 910 if (strcmp(argv[2], "-") && !globulize(&argv[2])) { 911 code = -1; 912 return; 913 } 914 globargv2 = argv[2]; 915 if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) || 916 !confirm("output to local-file:", argv[2]))) { 917 code = -1; 918 goto freels; 919 } 920 recvrequest(cmd, argv[2], argv[1], "w", 0, 0); 921 922 /* flush results in case commands are coming from a pipe */ 923 fflush(ttyout); 924 freels: 925 if (argv[2] != globargv2) /* free up after globulize() */ 926 free(argv[2]); 927 if (globargv2 != oldargv2) 928 free(globargv2); 929 } 930 931 /* 932 * Get a directory listing of multiple remote files. 933 */ 934 void 935 mls(int argc, char *argv[]) 936 { 937 sig_t oldintr; 938 int i; 939 char lmode[1], *dest, *odest; 940 941 if (argc < 2 && !another(&argc, &argv, "remote-files")) 942 goto usage; 943 if (argc < 3 && !another(&argc, &argv, "local-file")) { 944 usage: 945 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]); 946 code = -1; 947 return; 948 } 949 odest = dest = argv[argc - 1]; 950 argv[argc - 1] = NULL; 951 if (strcmp(dest, "-") && *dest != '|') 952 if (!globulize(&dest) || 953 !confirm("output to local-file:", dest)) { 954 code = -1; 955 return; 956 } 957 mname = argv[0]; 958 mflag = 1; 959 oldintr = signal(SIGINT, mabort); 960 (void)setjmp(jabort); 961 for (i = 1; mflag && i < argc-1; ++i) { 962 *lmode = (i == 1) ? 'w' : 'a'; 963 recvrequest("LIST", dest, argv[i], lmode, 0, 0); 964 if (!mflag && fromatty) { 965 if (confirm(argv[0], NULL)) 966 mflag ++; 967 } 968 } 969 (void)signal(SIGINT, oldintr); 970 mflag = 0; 971 if (dest != odest) /* free up after globulize() */ 972 free(dest); 973 } 974 975 /* 976 * Do a shell escape 977 */ 978 /*ARGSUSED*/ 979 void 980 shell(int argc, char *argv[]) 981 { 982 pid_t pid; 983 sig_t old1, old2; 984 char shellnam[PATH_MAX], *shellp, *namep; 985 int wait_status; 986 987 old1 = signal (SIGINT, SIG_IGN); 988 old2 = signal (SIGQUIT, SIG_IGN); 989 if ((pid = fork()) == 0) { 990 (void)closefrom(3); 991 (void)signal(SIGINT, SIG_DFL); 992 (void)signal(SIGQUIT, SIG_DFL); 993 shellp = getenv("SHELL"); 994 if (shellp == NULL || *shellp == '\0') 995 shellp = _PATH_BSHELL; 996 namep = strrchr(shellp, '/'); 997 if (namep == NULL) 998 namep = shellp; 999 shellnam[0] = '-'; 1000 (void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1); 1001 if (strcmp(namep, "sh") != 0) 1002 shellnam[0] = '+'; 1003 if (debug) { 1004 fputs(shellp, ttyout); 1005 fputc('\n', ttyout); 1006 (void)fflush(ttyout); 1007 } 1008 if (argc > 1) { 1009 execl(shellp, shellnam, "-c", altarg, (char *)NULL); 1010 } 1011 else { 1012 execl(shellp, shellnam, (char *)NULL); 1013 } 1014 warn("%s", shellp); 1015 code = -1; 1016 exit(1); 1017 } 1018 if (pid > 0) 1019 while (wait(&wait_status) != pid) 1020 ; 1021 (void)signal(SIGINT, old1); 1022 (void)signal(SIGQUIT, old2); 1023 if (pid == -1) { 1024 warn("Try again later"); 1025 code = -1; 1026 } 1027 else { 1028 code = 0; 1029 } 1030 } 1031 1032 /* 1033 * Send new user information (re-login) 1034 */ 1035 void 1036 user(int argc, char *argv[]) 1037 { 1038 char acctname[80]; 1039 int n, aflag = 0; 1040 1041 if (argc < 2) 1042 (void)another(&argc, &argv, "username"); 1043 if (argc < 2 || argc > 4) { 1044 fprintf(ttyout, "usage: %s username [password [account]]\n", 1045 argv[0]); 1046 code = -1; 1047 return; 1048 } 1049 n = command("USER %s", argv[1]); 1050 if (n == CONTINUE) { 1051 if (argc < 3 ) 1052 argv[2] = getpass("Password:"), argc++; 1053 n = command("PASS %s", argv[2]); 1054 } 1055 if (n == CONTINUE) { 1056 if (argc < 4) { 1057 (void)fputs("Account: ", ttyout); 1058 (void)fflush(ttyout); 1059 if (fgets(acctname, sizeof(acctname), stdin) == NULL) { 1060 clearerr(stdin); 1061 goto fail; 1062 } 1063 1064 acctname[strcspn(acctname, "\n")] = '\0'; 1065 1066 argv[3] = acctname; 1067 argc++; 1068 } 1069 n = command("ACCT %s", argv[3]); 1070 aflag++; 1071 } 1072 if (n != COMPLETE) { 1073 fail: 1074 fputs("Login failed.\n", ttyout); 1075 return; 1076 } 1077 if (!aflag && argc == 4) { 1078 (void)command("ACCT %s", argv[3]); 1079 } 1080 connected = -1; 1081 } 1082 1083 /* 1084 * Print working directory on remote machine. 1085 */ 1086 /*ARGSUSED*/ 1087 void 1088 pwd(int argc, char *argv[]) 1089 { 1090 int oldverbose = verbose; 1091 1092 /* 1093 * If we aren't verbose, this doesn't do anything! 1094 */ 1095 verbose = 1; 1096 if (command("PWD") == ERROR && code == 500) { 1097 fputs("PWD command not recognized, trying XPWD.\n", ttyout); 1098 (void)command("XPWD"); 1099 } 1100 verbose = oldverbose; 1101 } 1102 1103 /* 1104 * Print working directory on local machine. 1105 */ 1106 /* ARGSUSED */ 1107 void 1108 lpwd(int argc, char *argv[]) 1109 { 1110 char buf[PATH_MAX]; 1111 1112 if (getcwd(buf, sizeof(buf)) != NULL) 1113 fprintf(ttyout, "Local directory %s\n", buf); 1114 else 1115 warn("getcwd"); 1116 code = 0; 1117 } 1118 1119 /* 1120 * Make a directory. 1121 */ 1122 void 1123 makedir(int argc, char *argv[]) 1124 { 1125 1126 if ((argc < 2 && !another(&argc, &argv, "directory-name")) || 1127 argc > 2) { 1128 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1129 code = -1; 1130 return; 1131 } 1132 if (command("MKD %s", argv[1]) == ERROR && code == 500) { 1133 if (verbose) 1134 fputs("MKD command not recognized, trying XMKD.\n", ttyout); 1135 (void)command("XMKD %s", argv[1]); 1136 } 1137 } 1138 1139 /* 1140 * Remove a directory. 1141 */ 1142 void 1143 removedir(int argc, char *argv[]) 1144 { 1145 1146 if ((argc < 2 && !another(&argc, &argv, "directory-name")) || 1147 argc > 2) { 1148 fprintf(ttyout, "usage: %s directory-name\n", argv[0]); 1149 code = -1; 1150 return; 1151 } 1152 if (command("RMD %s", argv[1]) == ERROR && code == 500) { 1153 if (verbose) 1154 fputs("RMD command not recognized, trying XRMD.\n", ttyout); 1155 (void)command("XRMD %s", argv[1]); 1156 } 1157 } 1158 1159 /* 1160 * Send a line, verbatim, to the remote machine. 1161 */ 1162 void 1163 quote(int argc, char *argv[]) 1164 { 1165 1166 if (argc < 2 && !another(&argc, &argv, "command line to send")) { 1167 fprintf(ttyout, "usage: %s arg ...\n", argv[0]); 1168 code = -1; 1169 return; 1170 } 1171 quote1("", argc, argv); 1172 } 1173 1174 /* 1175 * Send a SITE command to the remote machine. The line 1176 * is sent verbatim to the remote machine, except that the 1177 * word "SITE" is added at the front. 1178 */ 1179 void 1180 site(int argc, char *argv[]) 1181 { 1182 1183 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) { 1184 fprintf(ttyout, "usage: %s arg ...\n", argv[0]); 1185 code = -1; 1186 return; 1187 } 1188 quote1("SITE", argc, argv); 1189 } 1190 1191 /* 1192 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1193 * Send the result as a one-line command and get response. 1194 */ 1195 void 1196 quote1(const char *initial, int argc, char *argv[]) 1197 { 1198 int i, len; 1199 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1200 1201 (void)strlcpy(buf, initial, sizeof(buf)); 1202 if (argc > 1) { 1203 for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) { 1204 /* Space for next arg */ 1205 if (len > 1) 1206 buf[len++] = ' '; 1207 1208 /* Sanity check */ 1209 if (len >= sizeof(buf) - 1) 1210 break; 1211 1212 /* Copy next argument, NUL terminate always */ 1213 strlcpy(&buf[len], argv[i], sizeof(buf) - len); 1214 1215 /* Update string length */ 1216 len = strlen(buf); 1217 } 1218 } 1219 1220 /* Make double (triple?) sure the sucker is NUL terminated */ 1221 buf[sizeof(buf) - 1] = '\0'; 1222 1223 if (command("%s", buf) == PRELIM) { 1224 while (getreply(0) == PRELIM) 1225 continue; 1226 } 1227 } 1228 1229 void 1230 do_chmod(int argc, char *argv[]) 1231 { 1232 1233 if (argc < 2 && !another(&argc, &argv, "mode")) 1234 goto usage; 1235 if ((argc < 3 && !another(&argc, &argv, "file")) || argc > 3) { 1236 usage: 1237 fprintf(ttyout, "usage: %s mode file\n", argv[0]); 1238 code = -1; 1239 return; 1240 } 1241 (void)command("SITE CHMOD %s %s", argv[1], argv[2]); 1242 } 1243 1244 void 1245 do_umask(int argc, char *argv[]) 1246 { 1247 int oldverbose = verbose; 1248 1249 verbose = 1; 1250 (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); 1251 verbose = oldverbose; 1252 } 1253 1254 void 1255 idle(int argc, char *argv[]) 1256 { 1257 int oldverbose = verbose; 1258 1259 verbose = 1; 1260 (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); 1261 verbose = oldverbose; 1262 } 1263 1264 /* 1265 * Ask the other side for help. 1266 */ 1267 void 1268 rmthelp(int argc, char *argv[]) 1269 { 1270 int oldverbose = verbose; 1271 1272 verbose = 1; 1273 (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]); 1274 verbose = oldverbose; 1275 } 1276 1277 /* 1278 * Terminate session and exit. 1279 */ 1280 /*ARGSUSED*/ 1281 void 1282 quit(int argc, char *argv[]) 1283 { 1284 1285 if (connected) 1286 disconnect(0, 0); 1287 pswitch(1); 1288 if (connected) { 1289 disconnect(0, 0); 1290 } 1291 exit(0); 1292 } 1293 1294 void 1295 account(int argc, char *argv[]) 1296 { 1297 char *ap; 1298 1299 if (argc > 2) { 1300 fprintf(ttyout, "usage: %s [password]\n", argv[0]); 1301 code = -1; 1302 return; 1303 } 1304 else if (argc == 2) 1305 ap = argv[1]; 1306 else 1307 ap = getpass("Account:"); 1308 (void)command("ACCT %s", ap); 1309 } 1310 1311 jmp_buf abortprox; 1312 1313 /* ARGSUSED */ 1314 void 1315 proxabort(int signo) 1316 { 1317 int save_errno = errno; 1318 1319 alarmtimer(0); 1320 if (!proxy) { 1321 pswitch(1); 1322 } 1323 if (connected) { 1324 proxflag = 1; 1325 } 1326 else { 1327 proxflag = 0; 1328 } 1329 pswitch(0); 1330 errno = save_errno; 1331 longjmp(abortprox, 1); 1332 } 1333 1334 void 1335 doproxy(int argc, char *argv[]) 1336 { 1337 struct cmd *c; 1338 int cmdpos; 1339 sig_t oldintr; 1340 1341 if (argc < 2 && !another(&argc, &argv, "command")) { 1342 fprintf(ttyout, "usage: %s command\n", argv[0]); 1343 code = -1; 1344 return; 1345 } 1346 c = getcmd(argv[1]); 1347 if (c == (struct cmd *) -1) { 1348 fputs("?Ambiguous command.\n", ttyout); 1349 (void)fflush(ttyout); 1350 code = -1; 1351 return; 1352 } 1353 if (c == 0) { 1354 fputs("?Invalid command.\n", ttyout); 1355 (void)fflush(ttyout); 1356 code = -1; 1357 return; 1358 } 1359 if (!c->c_proxy) { 1360 fputs("?Invalid proxy command.\n", ttyout); 1361 (void)fflush(ttyout); 1362 code = -1; 1363 return; 1364 } 1365 if (setjmp(abortprox)) { 1366 code = -1; 1367 return; 1368 } 1369 oldintr = signal(SIGINT, proxabort); 1370 pswitch(1); 1371 if (c->c_conn && !connected) { 1372 fputs("Not connected.\n", ttyout); 1373 (void)fflush(ttyout); 1374 pswitch(0); 1375 (void)signal(SIGINT, oldintr); 1376 code = -1; 1377 return; 1378 } 1379 cmdpos = strcspn(line, " \t"); 1380 if (cmdpos > 0) /* remove leading "proxy " from input buffer */ 1381 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); 1382 (*c->c_handler)(argc-1, argv+1); 1383 if (connected) { 1384 proxflag = 1; 1385 } 1386 else { 1387 proxflag = 0; 1388 } 1389 pswitch(0); 1390 (void)signal(SIGINT, oldintr); 1391 } 1392 1393 void 1394 setcase(int argc, char *argv[]) 1395 { 1396 1397 code = togglevar(argc, argv, &mcase, "Case mapping"); 1398 } 1399 1400 void 1401 setcr(int argc, char *argv[]) 1402 { 1403 1404 code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); 1405 } 1406 1407 void 1408 setntrans(int argc, char *argv[]) 1409 { 1410 if (argc == 1) { 1411 ntflag = 0; 1412 fputs("Ntrans off.\n", ttyout); 1413 code = ntflag; 1414 return; 1415 } 1416 ntflag++; 1417 code = ntflag; 1418 (void)strlcpy(ntin, argv[1], sizeof(ntin)); 1419 if (argc == 2) { 1420 ntout[0] = '\0'; 1421 return; 1422 } 1423 (void)strlcpy(ntout, argv[2], sizeof(ntout)); 1424 } 1425 1426 void 1427 setnmap(int argc, char *argv[]) 1428 { 1429 char *cp; 1430 1431 if (argc == 1) { 1432 mapflag = 0; 1433 fputs("Nmap off.\n", ttyout); 1434 code = mapflag; 1435 return; 1436 } 1437 if ((argc < 3 && !another(&argc, &argv, "outpattern")) || argc > 3) { 1438 fprintf(ttyout, "usage: %s [inpattern outpattern]\n", argv[0]); 1439 code = -1; 1440 return; 1441 } 1442 mapflag = 1; 1443 code = 1; 1444 cp = strchr(altarg, ' '); 1445 if (proxy) { 1446 while(*++cp == ' ') 1447 continue; 1448 altarg = cp; 1449 cp = strchr(altarg, ' '); 1450 } 1451 *cp = '\0'; 1452 (void)strncpy(mapin, altarg, PATH_MAX - 1); 1453 while (*++cp == ' ') 1454 continue; 1455 (void)strncpy(mapout, cp, PATH_MAX - 1); 1456 } 1457 1458 void 1459 setpassive(int argc, char *argv[]) 1460 { 1461 1462 code = togglevar(argc, argv, &passivemode, 1463 verbose ? "Passive mode" : NULL); 1464 } 1465 1466 void 1467 setsunique(int argc, char *argv[]) 1468 { 1469 1470 code = togglevar(argc, argv, &sunique, "Store unique"); 1471 } 1472 1473 void 1474 setrunique(int argc, char *argv[]) 1475 { 1476 1477 code = togglevar(argc, argv, &runique, "Receive unique"); 1478 } 1479 1480 /* change directory to parent directory */ 1481 /* ARGSUSED */ 1482 void 1483 cdup(int argc, char *argv[]) 1484 { 1485 int r; 1486 1487 r = command("CDUP"); 1488 if (r == ERROR && code == 500) { 1489 if (verbose) 1490 fputs("CDUP command not recognized, trying XCUP.\n", ttyout); 1491 r = command("XCUP"); 1492 } 1493 if (r == COMPLETE) 1494 dirchange = 1; 1495 } 1496 1497 /* 1498 * Restart transfer at specific point 1499 */ 1500 void 1501 restart(int argc, char *argv[]) 1502 { 1503 off_t nrestart_point; 1504 char *ep; 1505 1506 if (argc != 2) 1507 fputs("restart: offset not specified.\n", ttyout); 1508 else { 1509 nrestart_point = strtoll(argv[1], &ep, 10); 1510 if (nrestart_point == LLONG_MAX || *ep != '\0') 1511 fputs("restart: invalid offset.\n", ttyout); 1512 else { 1513 fprintf(ttyout, "Restarting at %lld. Execute get, put " 1514 "or append to initiate transfer\n", 1515 (long long)nrestart_point); 1516 restart_point = nrestart_point; 1517 } 1518 } 1519 } 1520 1521 /* 1522 * Show remote system type 1523 */ 1524 /* ARGSUSED */ 1525 void 1526 syst(int argc, char *argv[]) 1527 { 1528 1529 (void)command("SYST"); 1530 } 1531 1532 void 1533 macdef(int argc, char *argv[]) 1534 { 1535 char *tmp; 1536 int c; 1537 1538 if (macnum == 16) { 1539 fputs("Limit of 16 macros have already been defined.\n", ttyout); 1540 code = -1; 1541 return; 1542 } 1543 if ((argc < 2 && !another(&argc, &argv, "macro-name")) || argc > 2) { 1544 fprintf(ttyout, "usage: %s macro-name\n", argv[0]); 1545 code = -1; 1546 return; 1547 } 1548 if (interactive) 1549 fputs( 1550 "Enter macro line by line, terminating it with a null line.\n", ttyout); 1551 (void)strlcpy(macros[macnum].mac_name, argv[1], 1552 sizeof(macros[macnum].mac_name)); 1553 if (macnum == 0) 1554 macros[macnum].mac_start = macbuf; 1555 else 1556 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 1557 tmp = macros[macnum].mac_start; 1558 while (tmp != macbuf+4096) { 1559 if ((c = getchar()) == EOF) { 1560 fputs("macdef: end of file encountered.\n", ttyout); 1561 code = -1; 1562 return; 1563 } 1564 if ((*tmp = c) == '\n') { 1565 if (tmp == macros[macnum].mac_start) { 1566 macros[macnum++].mac_end = tmp; 1567 code = 0; 1568 return; 1569 } 1570 if (*(tmp-1) == '\0') { 1571 macros[macnum++].mac_end = tmp - 1; 1572 code = 0; 1573 return; 1574 } 1575 *tmp = '\0'; 1576 } 1577 tmp++; 1578 } 1579 while (1) { 1580 while ((c = getchar()) != '\n' && c != EOF) 1581 /* LOOP */; 1582 if (c == EOF || getchar() == '\n') { 1583 fputs("Macro not defined - 4K buffer exceeded.\n", ttyout); 1584 code = -1; 1585 return; 1586 } 1587 } 1588 } 1589 1590 /* 1591 * Get size of file on remote machine 1592 */ 1593 void 1594 sizecmd(int argc, char *argv[]) 1595 { 1596 off_t size; 1597 1598 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 1599 fprintf(ttyout, "usage: %s file\n", argv[0]); 1600 code = -1; 1601 return; 1602 } 1603 size = remotesize(argv[1], 1); 1604 if (size != -1) 1605 fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size); 1606 code = size; 1607 } 1608 1609 /* 1610 * Get last modification time of file on remote machine 1611 */ 1612 void 1613 modtime(int argc, char *argv[]) 1614 { 1615 time_t mtime; 1616 1617 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 1618 fprintf(ttyout, "usage: %s file\n", argv[0]); 1619 code = -1; 1620 return; 1621 } 1622 mtime = remotemodtime(argv[1], 1); 1623 if (mtime != -1) 1624 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); 1625 code = mtime; 1626 } 1627 1628 /* 1629 * Show status on remote machine 1630 */ 1631 void 1632 rmtstatus(int argc, char *argv[]) 1633 { 1634 1635 (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]); 1636 } 1637 1638 /* 1639 * Get file if modtime is more recent than current file 1640 */ 1641 void 1642 newer(int argc, char *argv[]) 1643 { 1644 1645 (void)getit(argc, argv, -1, "w"); 1646 } 1647 1648 /* 1649 * Display one file through $PAGER (defaults to "more"). 1650 */ 1651 void 1652 page(int argc, char *argv[]) 1653 { 1654 off_t orestart_point; 1655 int ohash, overbose; 1656 char *p, *pager, *oldargv1; 1657 1658 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { 1659 fprintf(ttyout, "usage: %s file\n", argv[0]); 1660 code = -1; 1661 return; 1662 } 1663 oldargv1 = argv[1]; 1664 if (!globulize(&argv[1])) { 1665 code = -1; 1666 return; 1667 } 1668 p = getenv("PAGER"); 1669 if (p == NULL || (*p == '\0')) 1670 p = PAGER; 1671 if (asprintf(&pager, "|%s", p) == -1) 1672 errx(1, "Can't allocate memory for $PAGER"); 1673 1674 orestart_point = restart_point; 1675 ohash = hash; 1676 overbose = verbose; 1677 restart_point = hash = verbose = 0; 1678 recvrequest("RETR", pager, argv[1], "r+w", 1, 0); 1679 (void)free(pager); 1680 restart_point = orestart_point; 1681 hash = ohash; 1682 verbose = overbose; 1683 if (oldargv1 != argv[1]) /* free up after globulize() */ 1684 free(argv[1]); 1685 } 1686 1687 #endif /* !SMALL */ 1688 1689