1/* 2 * Copyright (c) 1985, 1989 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34/* 35 * from: @(#)cmds.c 5.26 (Berkeley) 3/5/91 36 */ 37char cmds_rcsid[] = 38 "$Id: cmds.c,v 1.33 2000/07/23 01:36:59 dholland Exp $"; 39 40/* 41 * FTP User Program -- Command Routines. 42 */ 43#include <sys/types.h> 44#include <sys/wait.h> 45#include <sys/stat.h> 46#include <sys/socket.h> 47#include <netinet/in.h> 48 49#include <arpa/ftp.h> 50 51#include <signal.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <errno.h> 55#include <netdb.h> 56#include <ctype.h> 57#include <limits.h> /* for PATH_MAX */ 58#include <time.h> 59#include <string.h> 60#include <unistd.h> 61#ifdef __USE_READLINE__ 62#include <readline/readline.h> 63#include <readline/history.h> 64#endif 65 66#include "ftp_var.h" 67#include "pathnames.h" 68#include "cmds.h" 69#include "glob.h" 70 71void intr(int); 72 73extern FILE *cout; 74extern int data; 75extern const char *home; 76extern off_t restart_point; 77extern char reply_string[]; 78 79static char *mname; 80static sigjmp_buf jabort; 81static sigjmp_buf abortprox; 82 83static char *remglob(char *argv[], int doswitch); 84static int checkglob(int fd, const char *pattern); 85static char *dotrans(char *name); 86static char *domap(char *name); 87static char *globulize(char *str); 88static int confirm(const char *cmd, const char *file); 89static int getit(int argc, char *argv[], int restartit, const char *modestr); 90static void quote1(const char *initial, int argc, char **argv); 91 92 93/* 94 * pipeprotect: protect against "special" local filenames by prepending 95 * "./". Special local filenames are "-" and "|..." AND "/...". 96 */ 97static char *pipeprotect(char *name) 98{ 99 char *nu; 100 if (strcmp(name, "-") && *name!='|' && *name!='/') { 101 return name; 102 } 103 104 /* We're going to leak this memory. XXX. */ 105 nu = malloc(strlen(name)+3); 106 if (nu==NULL) { 107 perror("malloc"); 108 code = -1; 109 return NULL; 110 } 111 strcpy(nu, "."); 112 if (*name != '/') strcat(nu, "/"); 113 strcat(nu, name); 114 return nu; 115} 116 117/* 118 * Look for embedded ".." in a pathname and change it to "!!", printing 119 * a warning. 120 */ 121static char *pathprotect(char *name) 122{ 123 int gotdots=0, i, len; 124 125 /* Convert null terminator to trailing / to catch a trailing ".." */ 126 len = strlen(name)+1; 127 name[len-1] = '/'; 128 129 /* 130 * State machine loop. gotdots is < 0 if not looking at dots, 131 * 0 if we just saw a / and thus might start getting dots, 132 * and the count of dots seen so far if we have seen some. 133 */ 134 for (i=0; i<len; i++) { 135 if (name[i]=='.' && gotdots>=0) gotdots++; 136 else if (name[i]=='/' && gotdots<0) gotdots=0; 137 else if (name[i]=='/' && gotdots==2) { 138 printf("Warning: embedded .. in %.*s (changing to !!)\n", 139 len-1, name); 140 name[i-1] = '!'; 141 name[i-2] = '!'; 142 gotdots = 0; 143 } 144 else if (name[i]=='/') gotdots = 0; 145 else gotdots = -1; 146 } 147 name[len-1] = 0; 148 return name; 149} 150 151 152/* 153 * `Another' gets another argument, and stores the new argc and argv. 154 * It reverts to the top level (via main.c's intr()) on EOF/error. 155 * 156 * Returns false if no new arguments have been added. 157 */ 158int 159another(int *pargc, char ***pargv, const char *prompt) 160{ 161 int margc; 162 char **margv; 163 164 unsigned len = strlen(line); 165 int ret; 166 167 if (len >= sizeof(line) - 3) { 168 printf("sorry, arguments too long\n"); 169 intr(0); 170 } 171 printf("(%s) ", prompt); 172 line[len++] = ' '; 173 if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) 174 intr(0); 175 len += strlen(&line[len]); 176 if (len > 0 && line[len - 1] == '\n') 177 line[len - 1] = '\0'; 178 margv = makeargv(&margc, NULL); 179 ret = margc > *pargc; 180 *pargc = margc; 181 *pargv = margv; 182 return ret; 183} 184 185/* 186 * Connect to peer server and 187 * auto-login, if possible. 188 */ 189void 190setpeer(int argc, char *argv[]) 191{ 192 char *host; 193 unsigned short port; 194 195 if (connected) { 196 printf("Already connected to %s, use close first.\n", 197 hostname); 198 code = -1; 199 return; 200 } 201 if (argc < 2) 202 (void) another(&argc, &argv, "to"); 203 if (argc < 2 || argc > 3) { 204 printf("usage: %s host-name [port]\n", argv[0]); 205 code = -1; 206 return; 207 } 208 port = ftp_port; 209 if (argc > 2) { 210 port = atoi(argv[2]); 211 if (port < 1) { 212 printf("%s: bad port number-- %s\n", argv[1], argv[2]); 213 printf ("usage: %s host-name [port]\n", argv[0]); 214 code = -1; 215 return; 216 } 217 port = htons(port); 218 } 219 host = hookup(argv[1], port); 220 if (host) { 221 int overbose; 222 223 connected = 1; 224 /* 225 * Set up defaults for FTP. 226 */ 227 (void) strcpy(typename, "ascii"), type = TYPE_A; 228 curtype = TYPE_A; 229 (void) strcpy(formname, "non-print"), form = FORM_N; 230 (void) strcpy(modename, "stream"), mode = MODE_S; 231 (void) strcpy(structname, "file"), stru = STRU_F; 232 (void) strcpy(bytename, "8"), bytesize = 8; 233 if (autologin) 234 (void) dologin(argv[1]); 235 236#if defined(__unix__) && CHAR_BIT == 8 237/* 238 * this ifdef is to keep someone form "porting" this to an incompatible 239 * system and not checking this out. This way they have to think about it. 240 */ 241 overbose = verbose; 242 if (debug == 0) 243 verbose = -1; 244 if (command("SYST") == COMPLETE && overbose) { 245 register char *cp, c = 0; 246 cp = index(reply_string+4, ' '); 247 if (cp == NULL) 248 cp = index(reply_string+4, '\r'); 249 if (cp) { 250 if (cp[-1] == '.') 251 cp--; 252 c = *cp; 253 *cp = '\0'; 254 } 255 256 printf("Remote system type is %s.\n", 257 reply_string+4); 258 if (cp) 259 *cp = c; 260 } 261 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { 262 if (proxy) 263 unix_proxy = 1; 264 else 265 unix_server = 1; 266 /* 267 * Set type to 0 (not specified by user), 268 * meaning binary by default, but don't bother 269 * telling server. We can use binary 270 * for text files unless changed by the user. 271 */ 272 type = 0; 273 (void) strcpy(typename, "binary"); 274 if (overbose) 275 printf("Using %s mode to transfer files.\n", 276 typename); 277 } else { 278 if (proxy) 279 unix_proxy = 0; 280 else 281 unix_server = 0; 282 if (overbose && 283 !strncmp(reply_string, "215 TOPS20", 10)) 284 printf( 285"Remember to set tenex mode when transfering binary files from this machine.\n"); 286 } 287 verbose = overbose; 288#else 289#warning "Unix auto-mode code skipped" 290#endif /* unix */ 291 } 292} 293 294struct types { 295 const char *t_name; 296 const char *t_mode; 297 int t_type; 298 const char *t_arg; 299} types[] = { 300 { "ascii", "A", TYPE_A, NULL }, 301 { "binary", "I", TYPE_I, NULL }, 302 { "image", "I", TYPE_I, NULL }, 303 { "ebcdic", "E", TYPE_E, NULL }, 304 { "tenex", "L", TYPE_L, bytename }, 305 { NULL, NULL, 0, NULL } 306}; 307 308/* 309 * Set transfer type. 310 */ 311static 312void 313do_settype(const char *thetype) 314{ 315 struct types *p; 316 int comret; 317 318 for (p = types; p->t_name; p++) 319 if (strcmp(thetype, p->t_name) == 0) 320 break; 321 if (p->t_name == 0) { 322 printf("%s: unknown mode\n", thetype); 323 code = -1; 324 return; 325 } 326 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) 327 comret = command("TYPE %s %s", p->t_mode, p->t_arg); 328 else 329 comret = command("TYPE %s", p->t_mode); 330 if (comret == COMPLETE) { 331 (void) strcpy(typename, p->t_name); 332 curtype = type = p->t_type; 333 } 334} 335 336void 337settype(int argc, char *argv[]) 338{ 339 struct types *p; 340 if (argc > 2) { 341 const char *sep; 342 343 printf("usage: %s [", argv[0]); 344 sep = " "; 345 for (p = types; p->t_name; p++) { 346 printf("%s%s", sep, p->t_name); 347 sep = " | "; 348 } 349 printf(" ]\n"); 350 code = -1; 351 return; 352 } 353 if (argc < 2) { 354 printf("Using %s mode to transfer files.\n", typename); 355 code = 0; 356 return; 357 } 358 do_settype(argv[1]); 359} 360 361/* 362 * Internal form of settype; changes current type in use with server 363 * without changing our notion of the type for data transfers. 364 * Used to change to and from ascii for listings. 365 */ 366void 367changetype(int newtype, int show) 368{ 369 register struct types *p; 370 int comret, oldverbose = verbose; 371 int oldtick = tick; 372 373 if (newtype == 0) 374 newtype = TYPE_I; 375 if (newtype == curtype) 376 return; 377 if (debug == 0 && show == 0) 378 verbose = 0; 379 tick = 0; 380 for (p = types; p->t_name; p++) 381 if (newtype == p->t_type) 382 break; 383 if (p->t_name == 0) { 384 printf("ftp: internal error: unknown type %d\n", newtype); 385 return; 386 } 387 if (newtype == TYPE_L && bytename[0] != '\0') 388 comret = command("TYPE %s %s", p->t_mode, bytename); 389 else 390 comret = command("TYPE %s", p->t_mode); 391 if (comret == COMPLETE) 392 curtype = newtype; 393 verbose = oldverbose; 394 tick = oldtick; 395} 396 397/* 398 * Set binary transfer type. 399 */ 400/*VARARGS*/ 401void 402setbinary(void) 403{ 404 do_settype("binary"); 405} 406 407/* 408 * Set ascii transfer type. 409 */ 410/*VARARGS*/ 411void 412setascii(void) 413{ 414 do_settype("ascii"); 415} 416 417/* 418 * Set tenex transfer type. 419 */ 420/*VARARGS*/ 421void 422settenex(void) 423{ 424 do_settype("tenex"); 425} 426 427/* 428 * Set file transfer mode. 429 */ 430/*ARGSUSED*/ 431void 432setmode(void) 433{ 434 printf("We only support %s mode, sorry.\n", modename); 435 code = -1; 436} 437 438/* 439 * Set file transfer format. 440 */ 441/*ARGSUSED*/ 442void 443setform(void) 444{ 445 printf("We only support %s format, sorry.\n", formname); 446 code = -1; 447} 448 449/* 450 * Set file transfer structure. 451 */ 452void 453setstruct(void) 454{ 455 printf("We only support %s structure, sorry.\n", structname); 456 code = -1; 457} 458 459/* 460 * Send a single file. 461 */ 462void 463put(int argc, char *argv[]) 464{ 465 const char *cmd; 466 int loc = 0; 467 char *oldargv1, *oldargv2; 468 469 if (argc == 2) { 470 argc++; 471 argv[2] = argv[1]; 472 loc++; 473 } 474 if (argc < 2 && !another(&argc, &argv, "local-file")) 475 goto usage; 476 if (argc < 3 && !another(&argc, &argv, "remote-file")) { 477usage: 478 printf("usage: %s local-file remote-file\n", argv[0]); 479 code = -1; 480 return; 481 } 482 oldargv1 = argv[1]; 483 oldargv2 = argv[2]; 484 argv[1] = globulize(argv[1]); 485 if (!argv[1]) { 486 code = -1; 487 return; 488 } 489 /* 490 * If "globulize" modifies argv[1], and argv[2] is a copy of 491 * the old argv[1], make it a copy of the new argv[1]. 492 */ 493 if (argv[1] != oldargv1 && argv[2] == oldargv1) { 494 argv[2] = argv[1]; 495 } 496 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR"); 497 if (loc && ntflag) { 498 argv[2] = dotrans(argv[2]); 499 } 500 if (loc && mapflag) { 501 argv[2] = domap(argv[2]); 502 } 503 sendrequest(cmd, argv[1], argv[2], 504 argv[1] != oldargv1 || argv[2] != oldargv2); 505} 506 507void mabort(int); 508 509/* 510 * Send multiple files. 511 */ 512void 513mput(int argc, char *argv[]) 514{ 515 register int i; 516 void (*oldintr)(int); 517 int ointer; 518 char *tp; 519 520 if (argc < 2 && !another(&argc, &argv, "local-files")) { 521 printf("usage: %s local-files\n", argv[0]); 522 code = -1; 523 return; 524 } 525 mname = argv[0]; 526 mflag = 1; 527 oldintr = signal(SIGINT, mabort); 528 (void) sigsetjmp(jabort, 1); 529 if (proxy) { 530 char *cp, *tp2, tmpbuf[PATH_MAX]; 531 532 while ((cp = remglob(argv,0)) != NULL) { 533 if (*cp == 0) { 534 mflag = 0; 535 continue; 536 } 537 if (mflag && confirm(argv[0], cp)) { 538 tp = cp; 539 if (mcase) { 540 while (*tp && !islower(*tp)) { 541 tp++; 542 } 543 if (!*tp) { 544 tp = cp; 545 tp2 = tmpbuf; 546 while ((*tp2 = *tp) != '\0') { 547 if (isupper(*tp2)) { 548 *tp2 = 'a' + *tp2 - 'A'; 549 } 550 tp++; 551 tp2++; 552 } 553 } 554 tp = tmpbuf; 555 } 556 if (ntflag) { 557 tp = dotrans(tp); 558 } 559 if (mapflag) { 560 tp = domap(tp); 561 } 562 sendrequest((sunique) ? "STOU" : "STOR", 563 cp, tp, cp != tp || !interactive); 564 if (!mflag && fromatty) { 565 ointer = interactive; 566 interactive = 1; 567 if (confirm("Continue with","mput")) { 568 mflag++; 569 } 570 interactive = ointer; 571 } 572 } 573 } 574 (void) signal(SIGINT, oldintr); 575 mflag = 0; 576 return; 577 } 578 for (i = 1; i < argc; i++) { 579 register char **cpp, **gargs; 580 581 if (!doglob) { 582 if (mflag && confirm(argv[0], argv[i])) { 583 tp = (ntflag) ? dotrans(argv[i]) : argv[i]; 584 tp = (mapflag) ? domap(tp) : tp; 585 sendrequest((sunique) ? "STOU" : "STOR", 586 argv[i], tp, tp != argv[i] || !interactive); 587 if (!mflag && fromatty) { 588 ointer = interactive; 589 interactive = 1; 590 if (confirm("Continue with","mput")) { 591 mflag++; 592 } 593 interactive = ointer; 594 } 595 } 596 continue; 597 } 598 gargs = ftpglob(argv[i]); 599 if (globerr != NULL) { 600 printf("%s\n", globerr); 601 if (gargs) { 602 blkfree(gargs); 603 free((char *)gargs); 604 } 605 continue; 606 } 607 for (cpp = gargs; cpp && *cpp != NULL; cpp++) { 608 if (mflag && confirm(argv[0], *cpp)) { 609 tp = (ntflag) ? dotrans(*cpp) : *cpp; 610 tp = (mapflag) ? domap(tp) : tp; 611 sendrequest((sunique) ? "STOU" : "STOR", 612 *cpp, tp, *cpp != tp || !interactive); 613 if (!mflag && fromatty) { 614 ointer = interactive; 615 interactive = 1; 616 if (confirm("Continue with","mput")) { 617 mflag++; 618 } 619 interactive = ointer; 620 } 621 } 622 } 623 if (gargs != NULL) { 624 blkfree(gargs); 625 free((char *)gargs); 626 } 627 } 628 (void) signal(SIGINT, oldintr); 629 mflag = 0; 630} 631 632void 633reget(int argc, char *argv[]) 634{ 635 (void) getit(argc, argv, 1, "r+w"); 636} 637 638void 639get(int argc, char *argv[]) 640{ 641 (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" ); 642} 643 644/* 645 * Receive one file. 646 */ 647static int 648getit(int argc, char *argv[], int restartit, const char *modestr) 649{ 650 int loc = 0; 651 char *oldargv1, *oldargv2; 652 653 if (argc == 2) { 654 argc++; 655 /* 656 * Protect the user from accidentally retrieving special 657 * local names. 658 */ 659 argv[2] = pipeprotect(argv[1]); 660 if (!argv[2]) { 661 code = -1; 662 return 0; 663 } 664 loc++; 665 } 666 if (argc < 2 && !another(&argc, &argv, "remote-file")) 667 goto usage; 668 if (argc < 3 && !another(&argc, &argv, "local-file")) { 669usage: 670 printf("usage: %s remote-file [ local-file ]\n", argv[0]); 671 code = -1; 672 return (0); 673 } 674 oldargv1 = argv[1]; 675 oldargv2 = argv[2]; 676 argv[2] = globulize(argv[2]); 677 if (!argv[2]) { 678 code = -1; 679 return (0); 680 } 681 if (loc && mcase) { 682 char *tp = argv[1], *tp2, tmpbuf[PATH_MAX]; 683 684 while (*tp && !islower(*tp)) { 685 tp++; 686 } 687 if (!*tp) { 688 tp = argv[2]; 689 tp2 = tmpbuf; 690 while ((*tp2 = *tp) != '\0') { 691 if (isupper(*tp2)) { 692 *tp2 = 'a' + *tp2 - 'A'; 693 } 694 tp++; 695 tp2++; 696 } 697 argv[2] = tmpbuf; 698 } 699 } 700 if (loc && ntflag) 701 argv[2] = dotrans(argv[2]); 702 if (loc && mapflag) 703 argv[2] = domap(argv[2]); 704 if (restartit) { 705 struct stat stbuf; 706 int ret; 707 708 ret = stat(argv[2], &stbuf); 709 if (restartit == 1) { 710 if (ret < 0) { 711 fprintf(stderr, "local: %s: %s\n", argv[2], 712 strerror(errno)); 713 return (0); 714 } 715 restart_point = stbuf.st_size; 716 } else { 717 if (ret == 0) { 718 int overbose; 719 720 overbose = verbose; 721 if (debug == 0) 722 verbose = -1; 723 if (command("MDTM %s", argv[1]) == COMPLETE) { 724 int yy, mo, day, hour, min, sec; 725 struct tm *tm; 726 verbose = overbose; 727 sscanf(reply_string, 728 "%*s %04d%02d%02d%02d%02d%02d", 729 &yy, &mo, &day, &hour, &min, &sec); 730 tm = gmtime(&stbuf.st_mtime); 731 tm->tm_mon++; 732/* Indentation is misleading, but changes keep small. */ 733/* 734 * I think the indentation and braces are now correct. Whoever put this 735 * in the way it was originally should be prohibited by law. 736 */ 737 if (tm->tm_year+1900 > yy) 738 return (1); 739 if (tm->tm_year+1900 == yy) { 740 if (tm->tm_mon > mo) 741 return (1); 742 if (tm->tm_mon == mo) { 743 if (tm->tm_mday > day) 744 return (1); 745 if (tm->tm_mday == day) { 746 if (tm->tm_hour > hour) 747 return (1); 748 if (tm->tm_hour == hour) { 749 if (tm->tm_min > min) 750 return (1); 751 if (tm->tm_min == min) { 752 if (tm->tm_sec > sec) 753 return (1); 754 } 755 } 756 } 757 } 758 } 759 } else { 760 printf("%s\n", reply_string); 761 verbose = overbose; 762 return (0); 763 } 764 } 765 } 766 } 767 768 recvrequest("RETR", argv[2], argv[1], modestr, 769 argv[1] != oldargv1 || argv[2] != oldargv2); 770 restart_point = 0; 771 return (0); 772} 773 774void 775mabort(int ignore) 776{ 777 int ointer; 778 779 (void)ignore; 780 781 printf("\n"); 782 (void) fflush(stdout); 783 if (mflag && fromatty) { 784 ointer = interactive; 785 interactive = 1; 786 if (confirm("Continue with", mname)) { 787 interactive = ointer; 788 siglongjmp(jabort,0); 789 } 790 interactive = ointer; 791 } 792 mflag = 0; 793 siglongjmp(jabort,0); 794} 795 796/* 797 * Get multiple files. 798 */ 799void 800mget(int argc, char **argv) 801{ 802 void (*oldintr)(int); 803 int ointer; 804 char *cp, *tp, *tp2, tmpbuf[PATH_MAX]; 805 806 if (argc < 2 && !another(&argc, &argv, "remote-files")) { 807 printf("usage: %s remote-files\n", argv[0]); 808 code = -1; 809 return; 810 } 811 mname = argv[0]; 812 mflag = 1; 813 oldintr = signal(SIGINT,mabort); 814 (void) sigsetjmp(jabort, 1); 815 while ((cp = remglob(argv,proxy)) != NULL) { 816 if (*cp == '\0') { 817 mflag = 0; 818 continue; 819 } 820 if (mflag && confirm(argv[0], cp)) { 821 tp = cp; 822 if (mcase) { 823 while (*tp && !islower(*tp)) { 824 tp++; 825 } 826 if (!*tp) { 827 tp = cp; 828 tp2 = tmpbuf; 829 while ((*tp2 = *tp) != '\0') { 830 if (isupper(*tp2)) { 831 *tp2 = 'a' + *tp2 - 'A'; 832 } 833 tp++; 834 tp2++; 835 } 836 } 837 tp = tmpbuf; 838 } 839 if (ntflag) { 840 tp = dotrans(tp); 841 } 842 if (mapflag) { 843 tp = domap(tp); 844 } 845 /* Reject embedded ".." */ 846 tp = pathprotect(tp); 847 848 /* Prepend ./ to "-" or "!*" or leading "/" */ 849 tp = pipeprotect(tp); 850 if (tp == NULL) { 851 /* hmm... how best to handle this? */ 852 mflag = 0; 853 } 854 else { 855 recvrequest("RETR", tp, cp, "w", 856 tp != cp || !interactive); 857 } 858 if (!mflag && fromatty) { 859 ointer = interactive; 860 interactive = 1; 861 if (confirm("Continue with","mget")) { 862 mflag++; 863 } 864 interactive = ointer; 865 } 866 } 867 } 868 (void) signal(SIGINT,oldintr); 869 mflag = 0; 870} 871 872char * 873remglob(char *argv[], int doswitch) 874{ 875 char temp[16]; 876 static char buf[PATH_MAX]; 877 static FILE *ftemp = NULL; 878 static char **args; 879 int oldverbose, oldhash, badglob = 0; 880 char *cp; 881 882 if (!mflag) { 883 if (!doglob) { 884 args = NULL; 885 } 886 else { 887 if (ftemp) { 888 (void) fclose(ftemp); 889 ftemp = NULL; 890 } 891 } 892 return(NULL); 893 } 894 if (!doglob) { 895 if (args == NULL) 896 args = argv; 897 if ((cp = *++args) == NULL) 898 args = NULL; 899 return (cp); 900 } 901 if (ftemp == NULL) { 902 int oldumask, fd; 903 (void) strcpy(temp, _PATH_TMP); 904 905 /* libc 5.2.18 creates with mode 0666, which is dumb */ 906 oldumask = umask(077); 907 fd = mkstemp(temp); 908 umask(oldumask); 909 910 if (fd<0) { 911 printf("Error creating temporary file, oops\n"); 912 return NULL; 913 } 914 915 oldverbose = verbose, verbose = 0; 916 oldhash = hash, hash = 0; 917 if (doswitch) { 918 pswitch(!proxy); 919 } 920 while (*++argv != NULL) { 921 int dupfd = dup(fd); 922 923 recvrequest ("NLST", temp, *argv, "a", 0); 924 if (!checkglob(dupfd, *argv)) { 925 badglob = 1; 926 break; 927 } 928 } 929 unlink(temp); 930 931 if (doswitch) { 932 pswitch(!proxy); 933 } 934 verbose = oldverbose; hash = oldhash; 935 if (badglob) { 936 printf("Refusing to handle insecure file list\n"); 937 close(fd); 938 return NULL; 939 } 940 ftemp = fdopen(fd, "r"); 941 if (ftemp == NULL) { 942 printf("fdopen failed, oops\n"); 943 return NULL; 944 } 945 rewind(ftemp); 946 } 947 if (fgets(buf, sizeof (buf), ftemp) == NULL) { 948 (void) fclose(ftemp), ftemp = NULL; 949 return (NULL); 950 } 951 if ((cp = index(buf, '\n')) != NULL) 952 *cp = '\0'; 953 return (buf); 954} 955 956/* 957 * Check whether given pattern matches `..' 958 * We assume only a glob pattern starting with a dot will match 959 * dot entries on the server. 960 */ 961static int 962isdotdotglob(const char *pattern) 963{ 964 int havedot = 0; 965 char c; 966 967 if (*pattern++ != '.') 968 return 0; 969 while ((c = *pattern++) != '\0' && c != '/') { 970 if (c == '*' || c == '?') 971 continue; 972 if (c == '.' && havedot++) 973 return 0; 974 } 975 return 1; 976} 977 978/* 979 * This function makes sure the list of globbed files returned from 980 * the server doesn't contain anything dangerous such as 981 * /home/<yourname>/.forward, or ../.forward, 982 * or |mail foe@doe </etc/passwd, etc. 983 * Covered areas: 984 * - returned name starts with / but glob pattern doesn't 985 * - glob pattern starts with / but returned name doesn't 986 * - returned name starts with | 987 * - returned name contains .. in a position where glob 988 * pattern doesn't match .. 989 * I.e. foo/.* allows foo/../bar but not foo/.bar/../fly 990 * 991 * Note that globbed names starting with / should really be stored 992 * under the current working directory; this is handled in mget above. 993 * --okir 994 */ 995static int 996checkglob(int fd, const char *pattern) 997{ 998 const char *sp; 999 char buffer[MAXPATHLEN], dotdot[MAXPATHLEN]; 1000 int okay = 1, nrslash, initial, nr; 1001 FILE *fp; 1002 1003 /* Find slashes in glob pattern, and verify whether component 1004 * matches `..' 1005 */ 1006 initial = (pattern[0] == '/'); 1007 for (sp = pattern, nrslash = 0; sp != 0; sp = strchr(sp, '/')) { 1008 while (*sp == '/') 1009 sp++; 1010 if (nrslash >= MAXPATHLEN) { 1011 printf("Incredible pattern: %s\n", pattern); 1012 return 0; 1013 } 1014 dotdot[nrslash++] = isdotdotglob(sp); 1015 } 1016 1017 fp = fdopen(fd, "r"); 1018 while (okay && fgets(buffer, sizeof(buffer), fp) != NULL) { 1019 char *sp; 1020 1021 if ((sp = strchr(buffer, '\n')) != 0) { 1022 *sp = '\0'; 1023 } else { 1024 printf("Extremely long filename from server: %s", 1025 buffer); 1026 okay = 0; 1027 break; 1028 } 1029 if (buffer[0] == '|' 1030 || (buffer[0] != '/' && initial) 1031 || (buffer[0] == '/' && !initial)) 1032 okay = 0; 1033 for (sp = buffer, nr = 0; sp; sp = strchr(sp, '/'), nr++) { 1034 while (*sp == '/') 1035 sp++; 1036 if (sp[0] == '.' && !strncmp(sp, "../", 3) 1037 && (nr >= nrslash || !dotdot[nr])) 1038 okay = 0; 1039 } 1040 } 1041 1042 if (!okay) 1043 printf("Filename provided by server " 1044 "doesn't match pattern `%s': %s\n", pattern, buffer); 1045 1046 fclose(fp); 1047 return okay; 1048} 1049 1050static const char * 1051onoff(int bool) 1052{ 1053 return (bool ? "on" : "off"); 1054} 1055 1056/* 1057 * Show status. 1058 */ 1059void 1060status(void) 1061{ 1062 int i; 1063 1064 if (connected) 1065 printf("Connected to %s.\n", hostname); 1066 else 1067 printf("Not connected.\n"); 1068 if (!proxy) { 1069 pswitch(1); 1070 if (connected) { 1071 printf("Connected for proxy commands to %s.\n", hostname); 1072 } 1073 else { 1074 printf("No proxy connection.\n"); 1075 } 1076 pswitch(0); 1077 } 1078 printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n", 1079 modename, typename, formname, structname); 1080 printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n", 1081 onoff(verbose), onoff(bell), onoff(interactive), 1082 onoff(doglob)); 1083 printf("Store unique: %s; Receive unique: %s\n", onoff(sunique), 1084 onoff(runique)); 1085 printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag)); 1086 if (ntflag) { 1087 printf("Ntrans: (in) %s (out) %s\n", ntin,ntout); 1088 } 1089 else { 1090 printf("Ntrans: off\n"); 1091 } 1092 if (mapflag) { 1093 printf("Nmap: (in) %s (out) %s\n", mapin, mapout); 1094 } 1095 else { 1096 printf("Nmap: off\n"); 1097 } 1098 printf("Hash mark printing: %s; Use of PORT cmds: %s\n", 1099 onoff(hash), onoff(sendport)); 1100 printf("Tick counter printing: %s\n", onoff(tick)); 1101 if (macnum > 0) { 1102 printf("Macros:\n"); 1103 for (i=0; i<macnum; i++) { 1104 printf("\t%s\n",macros[i].mac_name); 1105 } 1106 } 1107 code = 0; 1108} 1109 1110/* 1111 * Set beep on cmd completed mode. 1112 */ 1113void 1114setbell(void) 1115{ 1116 1117 bell = !bell; 1118 printf("Bell mode %s.\n", onoff(bell)); 1119 code = bell; 1120} 1121 1122/* 1123 * Turn on packet tracing. 1124 */ 1125void 1126settrace(void) 1127{ 1128 traceflag = !traceflag; 1129 printf("Packet tracing %s.\n", onoff(traceflag)); 1130 code = traceflag; 1131} 1132 1133/* 1134 * Toggle hash mark printing during transfers. 1135 */ 1136void 1137sethash(void) 1138{ 1139 hash = !hash; 1140 if (hash && tick) 1141 settick(); 1142 1143 printf("Hash mark printing %s", onoff(hash)); 1144 code = hash; 1145 if (hash) 1146 printf(" (%d bytes/hash mark)", 1024); 1147 printf(".\n"); 1148} 1149 1150/* 1151 * Toggle tick counter printing during transfers. 1152 */ 1153void 1154settick(void) 1155{ 1156 tick = !tick; 1157 if (hash && tick) 1158 sethash(); 1159 printf("Tick counter printing %s", onoff(tick)); 1160 code = tick; 1161 if (tick) 1162 printf(" (%d bytes/tick increment)", TICKBYTES); 1163 printf(".\n"); 1164} 1165 1166/* 1167 * Turn on printing of server echos. 1168 */ 1169void 1170setverbose(void) 1171{ 1172 verbose = !verbose; 1173 printf("Verbose mode %s.\n", onoff(verbose)); 1174 code = verbose; 1175} 1176 1177/* 1178 * Toggle PORT cmd use before each data connection. 1179 */ 1180void 1181setport(void) 1182{ 1183 sendport = !sendport; 1184 printf("Use of PORT cmds %s.\n", onoff(sendport)); 1185 code = sendport; 1186} 1187 1188/* 1189 * Turn on interactive prompting 1190 * during mget, mput, and mdelete. 1191 */ 1192void 1193setprompt(void) 1194{ 1195 interactive = !interactive; 1196 printf("Interactive mode %s.\n", onoff(interactive)); 1197 code = interactive; 1198} 1199 1200/* 1201 * Toggle metacharacter interpretation 1202 * on local file names. 1203 */ 1204void 1205setglob(void) 1206{ 1207 doglob = !doglob; 1208 printf("Globbing %s.\n", onoff(doglob)); 1209 code = doglob; 1210} 1211 1212/* 1213 * Set debugging mode on/off and/or 1214 * set level of debugging. 1215 */ 1216void 1217setdebug(int argc, char *argv[]) 1218{ 1219 int val; 1220 1221 if (argc > 1) { 1222 val = atoi(argv[1]); 1223 if (val < 0) { 1224 printf("%s: bad debugging value.\n", argv[1]); 1225 code = -1; 1226 return; 1227 } 1228 } else 1229 val = !debug; 1230 debug = val; 1231 if (debug) 1232 options |= SO_DEBUG; 1233 else 1234 options &= ~SO_DEBUG; 1235 printf("Debugging %s (debug=%d).\n", onoff(debug), debug); 1236 code = debug > 0; 1237} 1238 1239/* 1240 * Set current working directory 1241 * on remote machine. 1242 */ 1243void 1244cd(int argc, char *argv[]) 1245{ 1246 1247 if (argc < 2 && !another(&argc, &argv, "remote-directory")) { 1248 printf("usage: %s remote-directory\n", argv[0]); 1249 code = -1; 1250 return; 1251 } 1252 if (command("CWD %s", argv[1]) == ERROR && code == 500) { 1253 if (verbose) 1254 printf("CWD command not recognized, trying XCWD\n"); 1255 (void) command("XCWD %s", argv[1]); 1256 } 1257} 1258 1259/* 1260 * Set current working directory 1261 * on local machine. 1262 */ 1263void 1264lcd(int argc, char *argv[]) 1265{ 1266 char buf[PATH_MAX]; 1267 const char *dir = NULL; 1268 1269 if (argc == 1) { 1270 /*dir = home;*/ 1271 dir = "."; 1272 } 1273 else if (argc != 2) { 1274 printf("usage: %s local-directory\n", argv[0]); 1275 code = -1; 1276 return; 1277 } 1278 else { 1279 dir = globulize(argv[1]); 1280 } 1281 if (!dir) { 1282 code = -1; 1283 return; 1284 } 1285 if (chdir(dir) < 0) { 1286 fprintf(stderr, "local: %s: %s\n", dir, strerror(errno)); 1287 code = -1; 1288 return; 1289 } 1290 if (!getcwd(buf, sizeof(buf))) { 1291 if (errno==ERANGE) strcpy(buf, "<too long>"); 1292 else strcpy(buf, "???"); 1293 } 1294 printf("Local directory now %s\n", buf); 1295 code = 0; 1296} 1297 1298/* 1299 * Delete a single file. 1300 */ 1301void 1302delete_cmd(int argc, char *argv[]) 1303{ 1304 1305 if (argc < 2 && !another(&argc, &argv, "remote-file")) { 1306 printf("usage: %s remote-file\n", argv[0]); 1307 code = -1; 1308 return; 1309 } 1310 (void) command("DELE %s", argv[1]); 1311} 1312 1313/* 1314 * Delete multiple files. 1315 */ 1316void 1317mdelete(int argc, char *argv[]) 1318{ 1319 void (*oldintr)(int); 1320 int ointer; 1321 char *cp; 1322 1323 if (argc < 2 && !another(&argc, &argv, "remote-files")) { 1324 printf("usage: %s remote-files\n", argv[0]); 1325 code = -1; 1326 return; 1327 } 1328 mname = argv[0]; 1329 mflag = 1; 1330 oldintr = signal(SIGINT, mabort); 1331 (void) sigsetjmp(jabort, 1); 1332 while ((cp = remglob(argv,0)) != NULL) { 1333 if (*cp == '\0') { 1334 mflag = 0; 1335 continue; 1336 } 1337 if (mflag && confirm(argv[0], cp)) { 1338 (void) command("DELE %s", cp); 1339 if (!mflag && fromatty) { 1340 ointer = interactive; 1341 interactive = 1; 1342 if (confirm("Continue with", "mdelete")) { 1343 mflag++; 1344 } 1345 interactive = ointer; 1346 } 1347 } 1348 } 1349 (void) signal(SIGINT, oldintr); 1350 mflag = 0; 1351} 1352 1353/* 1354 * Rename a remote file. 1355 */ 1356void 1357renamefile(int argc, char *argv[]) 1358{ 1359 1360 if (argc < 2 && !another(&argc, &argv, "from-name")) 1361 goto usage; 1362 if (argc < 3 && !another(&argc, &argv, "to-name")) { 1363usage: 1364 printf("%s from-name to-name\n", argv[0]); 1365 code = -1; 1366 return; 1367 } 1368 if (command("RNFR %s", argv[1]) == CONTINUE) 1369 (void) command("RNTO %s", argv[2]); 1370} 1371 1372/* 1373 * Get a directory listing 1374 * of remote files. 1375 */ 1376void 1377ls(int argc, char *argv[]) 1378{ 1379 static char foo[2] = "-"; 1380 const char *cmd; 1381 1382 if (argc < 2) { 1383 argc++, argv[1] = NULL; 1384 } 1385 if (argc < 3) { 1386 argc++, argv[2] = foo; 1387 } 1388 if (argc > 3) { 1389 printf("usage: %s remote-directory local-file\n", argv[0]); 1390 code = -1; 1391 return; 1392 } 1393 cmd = argv[0][0] == 'n' ? "NLST" : "LIST"; 1394 if (strcmp(argv[2], "-") && (argv[2] = globulize(argv[2]))==NULL) { 1395 code = -1; 1396 return; 1397 } 1398 if (strcmp(argv[2], "-") && *argv[2] != '|') 1399 if ((argv[2] = globulize(argv[2]))==NULL || 1400 !confirm("output to local-file:", argv[2])) { 1401 code = -1; 1402 return; 1403 } 1404 recvrequest(cmd, argv[2], argv[1], "w", 0); 1405} 1406 1407/* 1408 * Get a directory listing 1409 * of multiple remote files. 1410 */ 1411void 1412mls(int argc, char *argv[]) 1413{ 1414 void (*oldintr)(int); 1415 int ointer, i; 1416 const char *volatile cmd; 1417 char *volatile dest; 1418 const char *modestr; 1419 1420 if (argc < 2 && !another(&argc, &argv, "remote-files")) 1421 goto usage; 1422 if (argc < 3 && !another(&argc, &argv, "local-file")) { 1423usage: 1424 printf("usage: %s remote-files local-file\n", argv[0]); 1425 code = -1; 1426 return; 1427 } 1428 dest = argv[argc - 1]; 1429 argv[argc - 1] = NULL; 1430 if (strcmp(dest, "-") && *dest != '|') 1431 if ((dest = globulize(dest))==NULL || 1432 !confirm("output to local-file:", dest)) { 1433 code = -1; 1434 return; 1435 } 1436 cmd = argv[0][1] == 'l' ? "NLST" : "LIST"; 1437 mname = argv[0]; 1438 mflag = 1; 1439 oldintr = signal(SIGINT, mabort); 1440 1441 /* 1442 * This just plain seems wrong. 1443 */ 1444 (void) sigsetjmp(jabort, 1); 1445 1446 for (i = 1; mflag && i < argc-1; ++i) { 1447 modestr = (i == 1) ? "w" : "a"; 1448 recvrequest(cmd, dest, argv[i], modestr, 0); 1449 if (!mflag && fromatty) { 1450 ointer = interactive; 1451 interactive = 1; 1452 if (confirm("Continue with", argv[0])) { 1453 mflag ++; 1454 } 1455 interactive = ointer; 1456 } 1457 } 1458 (void) signal(SIGINT, oldintr); 1459 mflag = 0; 1460} 1461 1462/* 1463 * Do a shell escape 1464 */ 1465void 1466shell(const char *arg) 1467{ 1468 int pid; 1469 void (*old1)(int); 1470 void (*old2)(int); 1471 char shellnam[40]; 1472 const char *theshell, *namep; 1473 1474 old1 = signal (SIGINT, SIG_IGN); 1475 old2 = signal (SIGQUIT, SIG_IGN); 1476 if ((pid = fork()) == 0) { 1477 for (pid = 3; pid < 20; pid++) 1478 (void) close(pid); 1479 (void) signal(SIGINT, SIG_DFL); 1480 (void) signal(SIGQUIT, SIG_DFL); 1481 theshell = getenv("SHELL"); 1482 if (theshell == NULL) 1483 theshell = _PATH_BSHELL; 1484 namep = strrchr(theshell, '/'); 1485 if (namep == NULL) 1486 namep = theshell; 1487 else 1488 namep++; 1489 (void) strcpy(shellnam,"-"); 1490 (void) strcat(shellnam, namep); 1491 if (strcmp(namep, "sh") != 0) 1492 shellnam[0] = '+'; 1493 if (debug) { 1494 printf("%s\n", theshell); 1495 (void) fflush (stdout); 1496 } 1497 if (arg) { 1498 execl(theshell, shellnam, "-c", arg, NULL); 1499 } 1500 else { 1501 execl(theshell, shellnam, NULL); 1502 } 1503 perror(theshell); 1504 code = -1; 1505 exit(1); 1506 } 1507 if (pid > 0) while (wait(NULL) != pid); 1508 1509 (void) signal(SIGINT, old1); 1510 (void) signal(SIGQUIT, old2); 1511 if (pid == -1) { 1512 perror("Try again later"); 1513 code = -1; 1514 } 1515 else { 1516 code = 0; 1517 } 1518} 1519 1520/* 1521 * Send new user information (re-login) 1522 */ 1523void 1524user(int argc, char *argv[]) 1525{ 1526 char theacct[80]; 1527 int n, aflag = 0; 1528 1529 if (argc < 2) 1530 (void) another(&argc, &argv, "username"); 1531 if (argc < 2 || argc > 4) { 1532 printf("usage: %s username [password] [account]\n", argv[0]); 1533 code = -1; 1534 return; 1535 } 1536 n = command("USER %s", argv[1]); 1537 if (n == CONTINUE) { 1538 if (argc < 3 ) 1539 argv[2] = getpass("Password: "), argc++; 1540 n = command("PASS %s", argv[2]); 1541 } 1542 if (n == CONTINUE) { 1543 if (argc < 4) { 1544 printf("Account: "); (void) fflush(stdout); 1545 fgets(theacct, sizeof(theacct), stdin); 1546 argv[3] = theacct; argc++; 1547 } 1548 n = command("ACCT %s", argv[3]); 1549 aflag++; 1550 } 1551 if (n != COMPLETE) { 1552 fprintf(stdout, "Login failed.\n"); 1553 return; 1554 } 1555 if (!aflag && argc == 4) { 1556 (void) command("ACCT %s", argv[3]); 1557 } 1558} 1559 1560/* 1561 * Print working directory. 1562 */ 1563void 1564pwd(void) 1565{ 1566 int oldverbose = verbose; 1567 1568 /* 1569 * If we aren't verbose, this doesn't do anything! 1570 */ 1571 verbose = 1; 1572 if (command("PWD") == ERROR && code == 500) { 1573 printf("PWD command not recognized, trying XPWD\n"); 1574 (void) command("XPWD"); 1575 } 1576 verbose = oldverbose; 1577} 1578 1579/* 1580 * Make a directory. 1581 */ 1582void 1583makedir(int argc, char *argv[]) 1584{ 1585 1586 if (argc < 2 && !another(&argc, &argv, "directory-name")) { 1587 printf("usage: %s directory-name\n", argv[0]); 1588 code = -1; 1589 return; 1590 } 1591 if (command("MKD %s", argv[1]) == ERROR && code == 500) { 1592 if (verbose) 1593 printf("MKD command not recognized, trying XMKD\n"); 1594 (void) command("XMKD %s", argv[1]); 1595 } 1596} 1597 1598/* 1599 * Remove a directory. 1600 */ 1601void 1602removedir(int argc, char *argv[]) 1603{ 1604 1605 if (argc < 2 && !another(&argc, &argv, "directory-name")) { 1606 printf("usage: %s directory-name\n", argv[0]); 1607 code = -1; 1608 return; 1609 } 1610 if (command("RMD %s", argv[1]) == ERROR && code == 500) { 1611 if (verbose) 1612 printf("RMD command not recognized, trying XRMD\n"); 1613 (void) command("XRMD %s", argv[1]); 1614 } 1615} 1616 1617/* 1618 * Send a line, verbatim, to the remote machine. 1619 */ 1620void 1621quote(int argc, char *argv[]) 1622{ 1623 if (argc < 2 && !another(&argc, &argv, "command line to send")) { 1624 printf("usage: %s line-to-send\n", argv[0]); 1625 code = -1; 1626 return; 1627 } 1628 quote1("", argc, argv); 1629} 1630 1631/* 1632 * Send a SITE command to the remote machine. The line 1633 * is sent verbatim to the remote machine, except that the 1634 * word "SITE" is added at the front. 1635 */ 1636void 1637site(int argc, char *argv[]) 1638{ 1639 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) { 1640 printf("usage: %s line-to-send\n", argv[0]); 1641 code = -1; 1642 return; 1643 } 1644 quote1("SITE ", argc, argv); 1645} 1646 1647/* 1648 * Turn argv[1..argc) into a space-separated string, then prepend initial text. 1649 * Send the result as a one-line command and get response. 1650 */ 1651static void 1652quote1(const char *initial, int argc, char **argv) 1653{ 1654 register int i, len; 1655 char buf[BUFSIZ]; /* must be >= sizeof(line) */ 1656 1657 (void) strcpy(buf, initial); 1658 if (argc > 1) { 1659 len = strlen(buf); 1660 len += strlen(strcpy(&buf[len], argv[1])); 1661 for (i = 2; i < argc; i++) { 1662 buf[len++] = ' '; 1663 len += strlen(strcpy(&buf[len], argv[i])); 1664 } 1665 } 1666 if (command("%s", buf) == PRELIM) { 1667 while (getreply(0) == PRELIM); 1668 } 1669} 1670 1671void 1672do_chmod(int argc, char *argv[]) 1673{ 1674 1675 if (argc < 2 && !another(&argc, &argv, "mode")) 1676 goto usage; 1677 if (argc < 3 && !another(&argc, &argv, "file-name")) { 1678usage: 1679 printf("usage: %s mode file-name\n", argv[0]); 1680 code = -1; 1681 return; 1682 } 1683 (void) command("SITE CHMOD %s %s", argv[1], argv[2]); 1684} 1685 1686void 1687do_umask(int argc, char *argv[]) 1688{ 1689 int oldverbose = verbose; 1690 1691 verbose = 1; 1692 (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); 1693 verbose = oldverbose; 1694} 1695 1696void 1697idle_cmd(int argc, char *argv[]) 1698{ 1699 int oldverbose = verbose; 1700 1701 verbose = 1; 1702 (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); 1703 verbose = oldverbose; 1704} 1705 1706/* 1707 * Ask the other side for help. 1708 */ 1709void 1710rmthelp(int argc, char *argv[]) 1711{ 1712 int oldverbose = verbose; 1713 1714 verbose = 1; 1715 (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]); 1716 verbose = oldverbose; 1717} 1718 1719/* 1720 * Terminate session and exit. 1721 */ 1722void 1723quit(void) 1724{ 1725 1726 if (connected) 1727 disconnect(); 1728 pswitch(1); 1729 if (connected) { 1730 disconnect(); 1731 } 1732 exit(0); 1733} 1734 1735/* 1736 * Terminate session, but don't exit. 1737 */ 1738void 1739disconnect(void) 1740{ 1741 if (!connected) 1742 return; 1743 (void) command("QUIT"); 1744 if (cout) { 1745 (void) fclose(cout); 1746 } 1747 cout = NULL; 1748 connected = 0; 1749 data = -1; 1750 if (!proxy) { 1751 macnum = 0; 1752 } 1753} 1754 1755static int 1756confirm(const char *cmd, const char *file) 1757{ 1758 char lyne[BUFSIZ]; 1759 1760 if (!interactive) 1761 return (1); 1762 1763#ifdef __USE_READLINE__ 1764 if (fromatty && !rl_inhibit) { 1765 char *lineread; 1766 snprintf(lyne, BUFSIZ, "%s %s? ", cmd, file); 1767 lineread = readline(lyne); 1768 if (!lineread) return 0; 1769 strcpy(lyne, lineread); 1770 free(lineread); 1771 } 1772 else { 1773#endif 1774 printf("%s %s? ", cmd, file); 1775 fflush(stdout); 1776 if (fgets(lyne, sizeof(lyne), stdin) == NULL) { 1777 return 0; 1778 } 1779#ifdef __USE_READLINE__ 1780 } 1781#endif 1782 return (*lyne != 'n' && *lyne != 'N'); 1783} 1784 1785void 1786fatal(const char *msg) 1787{ 1788 1789 fprintf(stderr, "ftp: %s\n", msg); 1790 exit(1); 1791} 1792 1793/* 1794 * Glob a local file name specification with 1795 * the expectation of a single return value. 1796 * Can't control multiple values being expanded 1797 * from the expression, we return only the first. 1798 */ 1799static 1800char * 1801globulize(char *cpp) 1802{ 1803 char **globbed; 1804 char *rv = cpp; 1805 1806 if (!doglob) return cpp; 1807 1808 globbed = ftpglob(cpp); 1809 if (globerr != NULL) { 1810 printf("%s: %s\n", cpp, globerr); 1811 if (globbed) { 1812 blkfree(globbed); 1813 free(globbed); 1814 } 1815 return NULL; 1816 } 1817 if (globbed) { 1818 rv = globbed[0]; 1819 /* don't waste too much memory */ 1820 if (globbed[0]) { 1821 blkfree(globbed+1); 1822 } 1823 free(globbed); 1824 } 1825 return rv; 1826} 1827 1828void 1829account(int argc, char *argv[]) 1830{ 1831 char buf[128], *ap; 1832 1833 if (argc > 1) { 1834 *buf = 0; 1835 while (argc > 1) { 1836 --argc; 1837 ++argv; 1838 strncat(buf, *argv, sizeof(buf)-strlen(buf)); 1839 buf[sizeof(buf)-1] = 0; 1840 } 1841 ap = buf; 1842 } 1843 else { 1844 ap = getpass("Account:"); 1845 } 1846 command("ACCT %s", ap); 1847} 1848 1849static 1850void 1851proxabort(int ignore) 1852{ 1853 (void)ignore; 1854 1855 if (!proxy) { 1856 pswitch(1); 1857 } 1858 if (connected) { 1859 proxflag = 1; 1860 } 1861 else { 1862 proxflag = 0; 1863 } 1864 pswitch(0); 1865 siglongjmp(abortprox,1); 1866} 1867 1868void 1869doproxy(int argc, char *argv[]) 1870{ 1871 register struct cmd *c; 1872 void (*oldintr)(int); 1873 1874 if (argc < 2 && !another(&argc, &argv, "command")) { 1875 printf("usage: %s command\n", argv[0]); 1876 code = -1; 1877 return; 1878 } 1879 c = getcmd(argv[1]); 1880 if (c == (struct cmd *) -1) { 1881 printf("?Ambiguous command\n"); 1882 (void) fflush(stdout); 1883 code = -1; 1884 return; 1885 } 1886 if (c == 0) { 1887 printf("?Invalid command\n"); 1888 (void) fflush(stdout); 1889 code = -1; 1890 return; 1891 } 1892 if (!c->c_proxy) { 1893 printf("?Invalid proxy command\n"); 1894 (void) fflush(stdout); 1895 code = -1; 1896 return; 1897 } 1898 if (sigsetjmp(abortprox, 1)) { 1899 code = -1; 1900 return; 1901 } 1902 oldintr = signal(SIGINT, proxabort); 1903 pswitch(1); 1904 if (c->c_conn && !connected) { 1905 printf("Not connected\n"); 1906 (void) fflush(stdout); 1907 pswitch(0); 1908 (void) signal(SIGINT, oldintr); 1909 code = -1; 1910 return; 1911 } 1912 1913 if (c->c_handler_v) c->c_handler_v(argc-1, argv+1); 1914 else if (c->c_handler_0) c->c_handler_0(); 1915 else c->c_handler_1(NULL); /* should not reach this */ 1916 1917 if (connected) { 1918 proxflag = 1; 1919 } 1920 else { 1921 proxflag = 0; 1922 } 1923 pswitch(0); 1924 (void) signal(SIGINT, oldintr); 1925} 1926 1927void 1928setcase(void) 1929{ 1930 mcase = !mcase; 1931 printf("Case mapping %s.\n", onoff(mcase)); 1932 code = mcase; 1933} 1934 1935void 1936setcr(void) 1937{ 1938 crflag = !crflag; 1939 printf("Carriage Return stripping %s.\n", onoff(crflag)); 1940 code = crflag; 1941} 1942 1943void 1944setntrans(int argc, char *argv[]) 1945{ 1946 if (argc == 1) { 1947 ntflag = 0; 1948 printf("Ntrans off.\n"); 1949 code = ntflag; 1950 return; 1951 } 1952 ntflag++; 1953 code = ntflag; 1954 (void) strncpy(ntin, argv[1], 16); 1955 ntin[16] = '\0'; 1956 if (argc == 2) { 1957 ntout[0] = '\0'; 1958 return; 1959 } 1960 (void) strncpy(ntout, argv[2], 16); 1961 ntout[16] = '\0'; 1962} 1963 1964static char * 1965dotrans(char *name) 1966{ 1967 static char new[PATH_MAX]; 1968 char *cp1, *cp2 = new; 1969 register int i, ostop, found; 1970 1971 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++); 1972 for (cp1 = name; *cp1; cp1++) { 1973 found = 0; 1974 for (i = 0; *(ntin + i) && i < 16; i++) { 1975 if (*cp1 == *(ntin + i)) { 1976 found++; 1977 if (i < ostop) { 1978 *cp2++ = *(ntout + i); 1979 } 1980 break; 1981 } 1982 } 1983 if (!found) { 1984 *cp2++ = *cp1; 1985 } 1986 } 1987 *cp2 = '\0'; 1988 return(new); 1989} 1990 1991void 1992setnmap(int argc, char *argv[]) 1993{ 1994 char *cp; 1995 1996 if (argc == 1) { 1997 mapflag = 0; 1998 printf("Nmap off.\n"); 1999 code = mapflag; 2000 return; 2001 } 2002 if (argc < 3 && !another(&argc, &argv, "mapout")) { 2003 printf("Usage: %s [mapin mapout]\n",argv[0]); 2004 code = -1; 2005 return; 2006 } 2007 mapflag = 1; 2008 code = 1; 2009 cp = index(altarg, ' '); 2010 if (proxy) { 2011 while(*++cp == ' '); 2012 altarg = cp; 2013 cp = index(altarg, ' '); 2014 } 2015 *cp = '\0'; 2016 (void) strncpy(mapin, altarg, PATH_MAX - 1); 2017 mapin[PATH_MAX-1] = 0; 2018 while (*++cp == ' '); 2019 (void) strncpy(mapout, cp, PATH_MAX - 1); 2020 mapout[PATH_MAX-1] = 0; 2021} 2022 2023static 2024char * 2025domap(char *name) 2026{ 2027 static char new[PATH_MAX]; 2028 register char *cp1 = name, *cp2 = mapin; 2029 char *tp[9], *te[9]; 2030 int i, toks[9], toknum = 0, match = 1; 2031 2032 for (i=0; i < 9; ++i) { 2033 toks[i] = 0; 2034 } 2035 while (match && *cp1 && *cp2) { 2036 switch (*cp2) { 2037 case '\\': 2038 if (*++cp2 != *cp1) { 2039 match = 0; 2040 } 2041 break; 2042 case '$': 2043 if (*(cp2+1) >= '1' && *(cp2+1) <= '9') { 2044 if (*cp1 != *(++cp2+1)) { 2045 toknum = *cp2 - '1'; 2046 toks[toknum]++; 2047 tp[toknum] = cp1; 2048 while (*++cp1 && *(cp2+1) 2049 != *cp1); 2050 te[toknum] = cp1; 2051 } 2052 cp2++; 2053 break; 2054 } 2055 /* FALLTHROUGH */ 2056 default: 2057 if (*cp2 != *cp1) { 2058 match = 0; 2059 } 2060 break; 2061 } 2062 if (match && *cp1) { 2063 cp1++; 2064 } 2065 if (match && *cp2) { 2066 cp2++; 2067 } 2068 } 2069 if (!match && *cp1) /* last token mismatch */ 2070 { 2071 toks[toknum] = 0; 2072 } 2073 cp1 = new; 2074 *cp1 = '\0'; 2075 cp2 = mapout; 2076 while (*cp2) { 2077 match = 0; 2078 switch (*cp2) { 2079 case '\\': 2080 if (*(cp2 + 1)) { 2081 *cp1++ = *++cp2; 2082 } 2083 break; 2084 case '[': 2085LOOP: 2086 if (*++cp2 == '$' && isdigit(*(cp2+1))) { 2087 if (*++cp2 == '0') { 2088 char *cp3 = name; 2089 2090 while (*cp3) { 2091 *cp1++ = *cp3++; 2092 } 2093 match = 1; 2094 } 2095 else if (toks[toknum = *cp2 - '1']) { 2096 char *cp3 = tp[toknum]; 2097 2098 while (cp3 != te[toknum]) { 2099 *cp1++ = *cp3++; 2100 } 2101 match = 1; 2102 } 2103 } 2104 else { 2105 while (*cp2 && *cp2 != ',' && 2106 *cp2 != ']') { 2107 if (*cp2 == '\\') { 2108 cp2++; 2109 } 2110 else if (*cp2 == '$' && 2111 isdigit(*(cp2+1))) { 2112 if (*++cp2 == '0') { 2113 char *cp3 = name; 2114 2115 while (*cp3) { 2116 *cp1++ = *cp3++; 2117 } 2118 } 2119 else if (toks[toknum = 2120 *cp2 - '1']) { 2121 char *cp3=tp[toknum]; 2122 2123 while (cp3 != 2124 te[toknum]) { 2125 *cp1++ = *cp3++; 2126 } 2127 } 2128 } 2129 else if (*cp2) { 2130 *cp1++ = *cp2++; 2131 } 2132 } 2133 if (!*cp2) { 2134 printf("nmap: unbalanced brackets\n"); 2135 return(name); 2136 } 2137 match = 1; 2138 cp2--; 2139 } 2140 if (match) { 2141 while (*++cp2 && *cp2 != ']') { 2142 if (*cp2 == '\\' && *(cp2 + 1)) { 2143 cp2++; 2144 } 2145 } 2146 if (!*cp2) { 2147 printf("nmap: unbalanced brackets\n"); 2148 return(name); 2149 } 2150 break; 2151 } 2152 switch (*++cp2) { 2153 case ',': 2154 goto LOOP; 2155 case ']': 2156 break; 2157 default: 2158 cp2--; 2159 goto LOOP; 2160 } 2161 break; 2162 case '$': 2163 if (isdigit(*(cp2 + 1))) { 2164 if (*++cp2 == '0') { 2165 char *cp3 = name; 2166 2167 while (*cp3) { 2168 *cp1++ = *cp3++; 2169 } 2170 } 2171 else if (toks[toknum = *cp2 - '1']) { 2172 char *cp3 = tp[toknum]; 2173 2174 while (cp3 != te[toknum]) { 2175 *cp1++ = *cp3++; 2176 } 2177 } 2178 break; 2179 } 2180 /* intentional drop through */ 2181 default: 2182 *cp1++ = *cp2; 2183 break; 2184 } 2185 cp2++; 2186 } 2187 *cp1 = '\0'; 2188 if (!*new) { 2189 return(name); 2190 } 2191 return(new); 2192} 2193 2194void 2195setsunique(void) 2196{ 2197 sunique = !sunique; 2198 printf("Store unique %s.\n", onoff(sunique)); 2199 code = sunique; 2200} 2201 2202void 2203setrunique(void) 2204{ 2205 runique = !runique; 2206 printf("Receive unique %s.\n", onoff(runique)); 2207 code = runique; 2208} 2209 2210/* change directory to parent directory */ 2211void 2212cdup(void) 2213{ 2214 if (command("CDUP") == ERROR && code == 500) { 2215 if (verbose) 2216 printf("CDUP command not recognized, trying XCUP\n"); 2217 (void) command("XCUP"); 2218 } 2219} 2220 2221/* restart transfer at specific point */ 2222void 2223restart(int argc, char *argv[]) 2224{ 2225 if (argc != 2) 2226 printf("restart: offset not specified\n"); 2227 else { 2228 restart_point = atol(argv[1]); 2229 printf("restarting at %ld. %s\n", restart_point, 2230 "execute get, put or append to initiate transfer"); 2231 } 2232} 2233 2234/* show remote system type */ 2235void 2236syst(void) 2237{ 2238 command("SYST"); 2239} 2240 2241void 2242macdef(int argc, char *argv[]) 2243{ 2244 char *tmp; 2245 int c; 2246 2247 if (macnum == 16) { 2248 printf("Limit of 16 macros have already been defined\n"); 2249 code = -1; 2250 return; 2251 } 2252 if (argc < 2 && !another(&argc, &argv, "macro name")) { 2253 printf("Usage: %s macro_name\n",argv[0]); 2254 code = -1; 2255 return; 2256 } 2257 if (interactive) { 2258 printf("Enter macro line by line, terminating it with a null line\n"); 2259 } 2260 (void) strncpy(macros[macnum].mac_name, argv[1], 8); 2261 macros[macnum].mac_name[8] = 0; 2262 if (macnum == 0) { 2263 macros[macnum].mac_start = macbuf; 2264 } 2265 else { 2266 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; 2267 } 2268 tmp = macros[macnum].mac_start; 2269 /* stepping over the end of the array, remember to take away 1! */ 2270 while (tmp != macbuf+MACBUF_SIZE) { 2271 if ((c = getchar()) == EOF) { 2272 printf("macdef:end of file encountered\n"); 2273 code = -1; 2274 return; 2275 } 2276 if ((*tmp = c) == '\n') { 2277 if (tmp == macros[macnum].mac_start) { 2278 macros[macnum++].mac_end = tmp; 2279 code = 0; 2280 return; 2281 } 2282 if (*(tmp-1) == '\0') { 2283 macros[macnum++].mac_end = tmp - 1; 2284 code = 0; 2285 return; 2286 } 2287 *tmp = '\0'; 2288 } 2289 tmp++; 2290 } 2291 while (1) { 2292 while ((c = getchar()) != '\n' && c != EOF) 2293 /* LOOP */; 2294 if (c == EOF || getchar() == '\n') { 2295 printf("Macro not defined - 4k buffer exceeded\n"); 2296 code = -1; 2297 return; 2298 } 2299 } 2300} 2301 2302/* 2303 * Start up passive mode interaction 2304 */ 2305void 2306setpassive(void) 2307{ 2308 passivemode = !passivemode; 2309 printf("Passive mode %s.\n", onoff(passivemode)); 2310 code = passivemode; 2311} 2312 2313/* 2314 * get size of file on remote machine 2315 */ 2316void 2317sizecmd(int argc, char *argv[]) 2318{ 2319 2320 if (argc < 2 && !another(&argc, &argv, "filename")) { 2321 printf("usage: %s filename\n", argv[0]); 2322 code = -1; 2323 return; 2324 } 2325 (void) command("SIZE %s", argv[1]); 2326} 2327 2328/* 2329 * get last modification time of file on remote machine 2330 */ 2331void 2332modtime(int argc, char *argv[]) 2333{ 2334 int overbose; 2335 2336 if (argc < 2 && !another(&argc, &argv, "filename")) { 2337 printf("usage: %s filename\n", argv[0]); 2338 code = -1; 2339 return; 2340 } 2341 overbose = verbose; 2342 if (debug == 0) 2343 verbose = -1; 2344 if (command("MDTM %s", argv[1]) == COMPLETE) { 2345 int yy, mo, day, hour, min, sec; 2346 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, 2347 &day, &hour, &min, &sec); 2348 /* might want to print this in local time */ 2349 printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1], 2350 mo, day, yy, hour, min, sec); 2351 } else 2352 printf("%s\n", reply_string); 2353 verbose = overbose; 2354} 2355 2356/* 2357 * show status on remote machine 2358 */ 2359void 2360rmtstatus(int argc, char *argv[]) 2361{ 2362 (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]); 2363} 2364 2365/* 2366 * get file if modtime is more recent than current file 2367 */ 2368void 2369newer(int argc, char *argv[]) 2370{ 2371 if (getit(argc, argv, -1, "w")) { 2372 /* This should be controlled by some verbose flag */ 2373 printf("Local file \"%s\" is newer than remote file \"%s\"\n", 2374 argv[2], argv[1]); 2375 } 2376} 2377