1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ip.h> 5 #include <auth.h> 6 #include <fcall.h> 7 #include <ctype.h> 8 #include <String.h> 9 #include "ftpfs.h" 10 11 enum 12 { 13 /* return codes */ 14 Extra= 1, 15 Success= 2, 16 Incomplete= 3, 17 TempFail= 4, 18 PermFail= 5, 19 Impossible= 6, 20 }; 21 22 Node *remdir; /* current directory on remote machine */ 23 Node *remroot; /* root directory on remote machine */ 24 25 int ctlfd; /* fd for control connection */ 26 Biobuf ctlin; /* input buffer for control connection */ 27 Biobuf stdin; /* input buffer for standard input */ 28 Biobuf dbuf; /* buffer for data connection */ 29 char msg[512]; /* buffer for replies */ 30 char net[Maxpath]; /* network for connections */ 31 int listenfd; /* fd to listen on for connections */ 32 char netdir[Maxpath]; 33 int os, defos; 34 char topsdir[64]; /* name of listed directory for TOPS */ 35 String *remrootpath; /* path on remote side to remote root */ 36 char *user; 37 int nopassive; 38 long lastsend; 39 40 static void sendrequest(char*, char*); 41 static int getreply(Biobuf*, char*, int, int); 42 static int active(int, Biobuf**, char*, char*); 43 static int passive(int, Biobuf**, char*, char*); 44 static int data(int, Biobuf**, char*, char*); 45 static int port(void); 46 static void ascii(void); 47 static void image(void); 48 static void unixpath(Node*, String*); 49 static void vmspath(Node*, String*); 50 static void mvspath(Node*, String*); 51 static Node* vmsdir(char*); 52 static int getpassword(char*, char*); 53 54 /* 55 * connect to remote server, default network is "tcp/ip" 56 */ 57 void 58 hello(char *dest) 59 { 60 char *p; 61 char dir[Maxpath]; 62 63 Binit(&stdin, 0, OREAD); /* init for later use */ 64 65 ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0); 66 if(ctlfd < 0){ 67 fprint(2, "can't dial %s: %r\n", dest); 68 exits("dialing"); 69 } 70 Binit(&ctlin, ctlfd, OREAD); 71 72 /* remember network for the data connections */ 73 p = strrchr(dir+1, '/'); 74 if(p == 0) 75 fatal("wrong dial(2) linked with ftp"); 76 *p = 0; 77 safecpy(net, dir, sizeof(net)); 78 79 /* wait for hello from other side */ 80 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success) 81 fatal("bad hello"); 82 } 83 84 /* 85 * login to remote system 86 */ 87 void 88 rlogin(void) 89 { 90 char *line; 91 char pass[128]; 92 93 for(;;){ 94 print("User[default = %s]: ", user); 95 line = Brdline(&stdin, '\n'); 96 if(line == 0) 97 exits(0); 98 line[Blinelen(&stdin)-1] = 0; 99 if(*line){ 100 free(user); 101 user = strdup(line); 102 } 103 sendrequest("USER", user); 104 switch(getreply(&ctlin, msg, sizeof(msg), 1)){ 105 case Success: 106 return; 107 case Incomplete: 108 break; 109 case TempFail: 110 case PermFail: 111 continue; 112 } 113 114 if(getpassword(pass, pass+sizeof(pass)) < 0) 115 exits(0); 116 sendrequest("PASS", pass); 117 if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){ 118 if(strstr(msg, "Sess#")) 119 defos = MVS; 120 return; 121 } 122 } 123 } 124 125 /* 126 * login to remote system with given user name and password. 127 */ 128 void 129 clogin(char *cuser, char *cpassword) 130 { 131 free(user); 132 user = strdup(cuser); 133 if (strcmp(user, "anonymous") != 0 && 134 strcmp(user, "ftp") != 0) 135 fatal("User must be 'anonymous' or 'ftp'"); 136 sendrequest("USER", user); 137 switch(getreply(&ctlin, msg, sizeof(msg), 1)){ 138 case Success: 139 return; 140 case Incomplete: 141 break; 142 case TempFail: 143 case PermFail: 144 fatal("login failed"); 145 } 146 if (cpassword == 0) 147 fatal("password needed"); 148 sendrequest("PASS", cpassword); 149 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success) 150 fatal("password failed"); 151 if(strstr(msg, "Sess#")) 152 defos = MVS; 153 return; 154 } 155 156 /* 157 * find out about the other side. go to it's root if requested. set 158 * image mode if a Plan9 system. 159 */ 160 void 161 preamble(char *mountroot) 162 { 163 char *p, *ep; 164 int rv; 165 OS *o; 166 167 /* 168 * create a root directory mirror 169 */ 170 remroot = newnode(0, s_copy("/")); 171 remroot->d->qid.type = QTDIR; 172 remroot->d->mode = DMDIR|0777; 173 remdir = remroot; 174 175 /* 176 * get system type 177 */ 178 sendrequest("SYST", nil); 179 switch(getreply(&ctlin, msg, sizeof(msg), 1)){ 180 case Success: 181 for(o = oslist; o->os != Unknown; o++) 182 if(strncmp(msg+4, o->name, strlen(o->name)) == 0) 183 break; 184 os = o->os; 185 if(os == NT) 186 os = Unix; 187 break; 188 default: 189 os = defos; 190 break; 191 } 192 if(os == Unknown) 193 os = defos; 194 195 remrootpath = s_reset(remrootpath); 196 switch(os){ 197 case Unix: 198 case Plan9: 199 case NetWare: 200 /* 201 * go to the remote root, if asked 202 */ 203 if(mountroot){ 204 sendrequest("CWD", mountroot); 205 getreply(&ctlin, msg, sizeof(msg), 0); 206 } else { 207 s_append(remrootpath, "/usr/"); 208 s_append(remrootpath, user); 209 } 210 211 /* 212 * get the root directory 213 */ 214 sendrequest("PWD", nil); 215 rv = getreply(&ctlin, msg, sizeof(msg), 1); 216 if(rv == PermFail){ 217 sendrequest("XPWD", nil); 218 rv = getreply(&ctlin, msg, sizeof(msg), 1); 219 } 220 if(rv == Success){ 221 p = strchr(msg, '"'); 222 if(p){ 223 p++; 224 ep = strchr(p, '"'); 225 if(ep){ 226 *ep = 0; 227 s_append(s_reset(remrootpath), p); 228 } 229 } 230 } 231 232 break; 233 case Tops: 234 case VM: 235 /* 236 * top directory is a figment of our imagination. 237 * make it permanently cached & valid. 238 */ 239 CACHED(remroot); 240 VALID(remroot); 241 remroot->d->atime = time(0) + 100000; 242 243 /* 244 * no initial directory. We are in the 245 * imaginary root. 246 */ 247 remdir = newtopsdir("???"); 248 topsdir[0] = 0; 249 if(os == Tops && readdir(remdir) >= 0){ 250 CACHED(remdir); 251 if(*topsdir) 252 remdir->remname = s_copy(topsdir); 253 VALID(remdir); 254 } 255 break; 256 case VMS: 257 /* 258 * top directory is a figment of our imagination. 259 * make it permanently cached & valid. 260 */ 261 CACHED(remroot); 262 VALID(remroot); 263 remroot->d->atime = time(0) + 100000; 264 265 /* 266 * get current directory 267 */ 268 sendrequest("PWD", nil); 269 rv = getreply(&ctlin, msg, sizeof(msg), 1); 270 if(rv == PermFail){ 271 sendrequest("XPWD", nil); 272 rv = getreply(&ctlin, msg, sizeof(msg), 1); 273 } 274 if(rv == Success){ 275 p = strchr(msg, '"'); 276 if(p){ 277 p++; 278 ep = strchr(p, '"'); 279 if(ep){ 280 *ep = 0; 281 remroot = remdir = vmsdir(p); 282 } 283 } 284 } 285 break; 286 case MVS: 287 usenlst = 1; 288 break; 289 } 290 291 if(os == Plan9) 292 image(); 293 } 294 295 static void 296 ascii(void) 297 { 298 sendrequest("TYPE A", nil); 299 switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 300 case Success: 301 break; 302 default: 303 fatal("can't set type to ascii"); 304 } 305 } 306 307 static void 308 image(void) 309 { 310 sendrequest("TYPE I", nil); 311 switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 312 case Success: 313 break; 314 default: 315 fatal("can't set type to image/binary"); 316 } 317 } 318 319 /* 320 * decode the time fields, return seconds since epoch began 321 */ 322 char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec"; 323 static Tm now; 324 325 static ulong 326 cracktime(char *month, char *day, char *yr, char *hms) 327 { 328 Tm tm; 329 int i; 330 char *p; 331 332 /* default time */ 333 if(now.year == 0) 334 now = *localtime(time(0)); 335 tm = now; 336 337 /* convert ascii month to a number twixt 1 and 12 */ 338 if(*month >= '0' && *month <= '9'){ 339 tm.mon = atoi(month) - 1; 340 if(tm.mon < 0 || tm.mon > 11) 341 tm.mon = 5; 342 } else { 343 for(p = month; *p; p++) 344 *p = tolower(*p); 345 for(i = 0; i < 12; i++) 346 if(strncmp(&monthchars[i*3], month, 3) == 0){ 347 tm.mon = i; 348 break; 349 } 350 } 351 352 tm.mday = atoi(day); 353 354 if(hms){ 355 tm.hour = strtol(hms, &p, 0); 356 if(*p == ':'){ 357 tm.min = strtol(p+1, &p, 0); 358 if(*p == ':') 359 tm.sec = strtol(p+1, &p, 0); 360 } 361 if(tolower(*p) == 'p') 362 tm.hour += 12; 363 } 364 365 if(yr){ 366 tm.year = atoi(yr); 367 if(tm.year >= 1900) 368 tm.year -= 1900; 369 } else { 370 if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1)) 371 tm.year--; 372 } 373 374 /* convert to epoch seconds */ 375 return tm2sec(&tm); 376 } 377 378 /* 379 * decode a Unix or Plan 9 file mode 380 */ 381 static ulong 382 crackmode(char *p) 383 { 384 ulong flags; 385 ulong mode; 386 int i; 387 388 flags = 0; 389 switch(strlen(p)){ 390 case 10: /* unix and new style plan 9 */ 391 switch(*p){ 392 case 'l': 393 return DMSYML|0777; 394 case 'd': 395 flags |= DMDIR; 396 case 'a': 397 flags |= DMAPPEND; 398 } 399 p++; 400 if(p[2] == 'l') 401 flags |= DMEXCL; 402 break; 403 case 11: /* old style plan 9 */ 404 switch(*p++){ 405 case 'd': 406 flags |= DMDIR; 407 break; 408 case 'a': 409 flags |= DMAPPEND; 410 break; 411 } 412 if(*p++ == 'l') 413 flags |= DMEXCL; 414 break; 415 default: 416 return DMDIR|0777; 417 } 418 mode = 0; 419 for(i = 0; i < 3; i++){ 420 mode <<= 3; 421 if(*p++ == 'r') 422 mode |= DMREAD; 423 if(*p++ == 'w') 424 mode |= DMWRITE; 425 if(*p == 'x' || *p == 's' || *p == 'S') 426 mode |= DMEXEC; 427 p++; 428 } 429 return mode | flags; 430 } 431 432 /* 433 * find first punctuation 434 */ 435 char* 436 strpunct(char *p) 437 { 438 int c; 439 440 for(;c = *p; p++){ 441 if(ispunct(c)) 442 return p; 443 } 444 return 0; 445 } 446 447 /* 448 * decode a Unix or Plan 9 directory listing 449 */ 450 static Dir* 451 crackdir(char *p, String **remname) 452 { 453 char *field[15]; 454 char *dfield[4]; 455 char *cp; 456 String *s; 457 int dn, n; 458 Dir d, *dp; 459 460 memset(&d, 0, sizeof(d)); 461 462 n = getfields(p, field, 15, 1, " \t"); 463 if(n > 2 && strcmp(field[n-2], "->") == 0) 464 n -= 2; 465 switch(os){ 466 case TSO: 467 cp = strchr(field[0], '.'); 468 if(cp){ 469 *cp++ = 0; 470 s = s_copy(cp); 471 d.uid = field[0]; 472 } else { 473 s = s_copy(field[0]); 474 d.uid = "TSO"; 475 } 476 d.gid = "TSO"; 477 d.mode = 0666; 478 d.length = 0; 479 d.atime = 0; 480 break; 481 case OS½: 482 s = s_copy(field[n-1]); 483 d.uid = "OS½"; 484 d.gid = d.uid; 485 d.mode = 0666; 486 switch(n){ 487 case 5: 488 if(strcmp(field[1], "DIR") == 0) 489 d.mode |= DMDIR; 490 d.length = atoi(field[0]); 491 dn = getfields(field[2], dfield, 4, 1, "-"); 492 if(dn == 3) 493 d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]); 494 break; 495 } 496 break; 497 case Tops: 498 if(n != 4){ /* tops directory name */ 499 safecpy(topsdir, field[0], sizeof(topsdir)); 500 return 0; 501 } 502 s = s_copy(field[3]); 503 d.length = atoi(field[0]); 504 d.mode = 0666; 505 d.uid = "Tops"; 506 d.gid = d.uid; 507 dn = getfields(field[1], dfield, 4, 1, "-"); 508 if(dn == 3) 509 d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]); 510 else 511 d.atime = time(0); 512 break; 513 case VM: 514 switch(n){ 515 case 9: 516 s = s_copy(field[0]); 517 s_append(s, "."); 518 s_append(s, field[1]); 519 d.length = atoi(field[3])*atoi(field[4]); 520 if(*field[2] == 'F') 521 d.mode = 0666; 522 else 523 d.mode = 0777; 524 d.uid = "VM"; 525 d.gid = d.uid; 526 dn = getfields(field[6], dfield, 4, 1, "/-"); 527 if(dn == 3) 528 d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]); 529 else 530 d.atime = time(0); 531 break; 532 case 1: 533 s = s_copy(field[0]); 534 d.uid = "VM"; 535 d.gid = d.uid; 536 d.mode = 0777; 537 d.atime = 0; 538 break; 539 default: 540 return nil; 541 } 542 break; 543 case VMS: 544 switch(n){ 545 case 6: 546 for(cp = field[0]; *cp; cp++) 547 *cp = tolower(*cp); 548 cp = strchr(field[0], ';'); 549 if(cp) 550 *cp = 0; 551 d.mode = 0666; 552 cp = field[0] + strlen(field[0]) - 4; 553 if(strcmp(cp, ".dir") == 0){ 554 d.mode |= DMDIR; 555 *cp = 0; 556 } 557 s = s_copy(field[0]); 558 d.length = atoi(field[1])*512; 559 field[4][strlen(field[4])-1] = 0; 560 d.uid = field[4]+1; 561 d.gid = d.uid; 562 dn = getfields(field[2], dfield, 4, 1, "/-"); 563 if(dn == 3) 564 d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]); 565 else 566 d.atime = time(0); 567 break; 568 default: 569 return nil; 570 } 571 break; 572 case NetWare: 573 switch(n){ 574 case 9: 575 s = s_copy(field[8]); 576 d.uid = field[2]; 577 d.gid = d.uid; 578 d.mode = 0666; 579 if(*field[0] == 'd') 580 d.mode |= DMDIR; 581 d.length = atoi(field[3]); 582 d.atime = cracktime(field[4], field[5], field[6], field[7]); 583 break; 584 case 1: 585 s = s_copy(field[0]); 586 d.uid = "none"; 587 d.gid = d.uid; 588 d.mode = 0777; 589 d.atime = 0; 590 break; 591 default: 592 return nil; 593 } 594 break; 595 case Unix: 596 case Plan9: 597 default: 598 switch(n){ 599 case 8: /* ls -l */ 600 s = s_copy(field[7]); 601 d.uid = field[2]; 602 d.gid = d.uid; 603 d.mode = crackmode(field[0]); 604 d.length = atoi(field[3]); 605 if(strchr(field[6], ':')) 606 d.atime = cracktime(field[4], field[5], 0, field[6]); 607 else 608 d.atime = cracktime(field[4], field[5], field[6], 0); 609 break; 610 case 9: /* ls -lg */ 611 s = s_copy(field[8]); 612 d.uid = field[2]; 613 d.gid = field[3]; 614 d.mode = crackmode(field[0]); 615 d.length = atoi(field[4]); 616 if(strchr(field[7], ':')) 617 d.atime = cracktime(field[5], field[6], 0, field[7]); 618 else 619 d.atime = cracktime(field[5], field[6], field[7], 0); 620 break; 621 case 10: /* plan 9 */ 622 s = s_copy(field[9]); 623 d.uid = field[3]; 624 d.gid = field[4]; 625 d.mode = crackmode(field[0]); 626 d.length = atoi(field[5]); 627 if(strchr(field[8], ':')) 628 d.atime = cracktime(field[6], field[7], 0, field[8]); 629 else 630 d.atime = cracktime(field[6], field[7], field[8], 0); 631 break; 632 case 4: /* a Windows_NT version */ 633 s = s_copy(field[3]); 634 d.uid = "NT"; 635 d.gid = d.uid; 636 if(strcmp("<DIR>", field[2]) == 0){ 637 d.length = 0; 638 d.mode = DMDIR|0777; 639 } else { 640 d.mode = 0666; 641 d.length = atoi(field[2]); 642 } 643 dn = getfields(field[0], dfield, 4, 1, "/-"); 644 if(dn == 3) 645 d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]); 646 break; 647 case 1: 648 s = s_copy(field[0]); 649 d.uid = "none"; 650 d.gid = d.uid; 651 d.mode = 0777; 652 d.atime = 0; 653 break; 654 default: 655 return nil; 656 } 657 } 658 d.muid = d.uid; 659 d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE; 660 d.mtime = d.atime; 661 if(ext && (d.qid.type & QTDIR) == 0) 662 s_append(s, ext); 663 d.name = s_to_c(s); 664 665 /* allocate a freeable dir structure */ 666 dp = reallocdir(&d, 0); 667 *remname = s; 668 669 return dp; 670 } 671 672 /* 673 * probe files in a directory to see if they are directories 674 */ 675 /* 676 * read a remote directory 677 */ 678 int 679 readdir(Node *node) 680 { 681 Biobuf *bp; 682 char *line; 683 Node *np; 684 Dir *d; 685 long n; 686 int tries, x, files; 687 static int uselist; 688 int usenlist; 689 String *remname; 690 691 if(changedir(node) < 0) 692 return -1; 693 694 usenlist = 0; 695 for(tries = 0; tries < 3; tries++){ 696 if(usenlist || usenlst) 697 x = data(OREAD, &bp, "NLST", nil); 698 else if(os == Unix && !uselist) 699 x = data(OREAD, &bp, "LIST -l", nil); 700 else 701 x = data(OREAD, &bp, "LIST", nil); 702 switch(x){ 703 case Extra: 704 break; 705 /* case TempFail: 706 continue; 707 */ 708 default: 709 if(os == Unix && uselist == 0){ 710 uselist = 1; 711 continue; 712 } 713 return seterr(nosuchfile); 714 } 715 files = 0; 716 while(line = Brdline(bp, '\n')){ 717 n = Blinelen(bp); 718 if(debug) 719 write(2, line, n); 720 if(n > 1 && line[n-2] == '\r') 721 n--; 722 line[n - 1] = 0; 723 724 d = crackdir(line, &remname); 725 if(d == nil) 726 continue; 727 files++; 728 np = extendpath(node, remname); 729 d->qid.path = np->d->qid.path; 730 d->qid.vers = np->d->qid.vers; 731 d->type = np->d->type; 732 d->dev = 1; /* mark node as valid */ 733 if(os == MVS && node == remroot){ 734 d->qid.type = QTDIR; 735 d->mode |= DMDIR; 736 } 737 free(np->d); 738 np->d = d; 739 } 740 close(Bfildes(bp)); 741 742 switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 743 case Success: 744 if(files == 0 && !usenlst && !usenlist){ 745 usenlist = 1; 746 continue; 747 } 748 if(files && usenlist) 749 usenlst = 1; 750 if(usenlst) 751 node->chdirunknown = 1; 752 return 0; 753 case TempFail: 754 break; 755 default: 756 return seterr(nosuchfile); 757 } 758 } 759 return seterr(nosuchfile); 760 } 761 762 /* 763 * create a remote directory 764 */ 765 int 766 createdir(Node *node) 767 { 768 if(changedir(node->parent) < 0) 769 return -1; 770 771 sendrequest("MKD", node->d->name); 772 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 773 return -1; 774 return 0; 775 } 776 777 /* 778 * change to a remote directory. 779 */ 780 int 781 changedir(Node *node) 782 { 783 Node *to; 784 String *cdpath; 785 786 to = node; 787 if(to == remdir) 788 return 0; 789 790 /* build an absolute path */ 791 switch(os){ 792 case Tops: 793 case VM: 794 switch(node->depth){ 795 case 0: 796 remdir = node; 797 return 0; 798 case 1: 799 cdpath = s_clone(node->remname); 800 break; 801 default: 802 return seterr(nosuchfile); 803 } 804 break; 805 case VMS: 806 switch(node->depth){ 807 case 0: 808 remdir = node; 809 return 0; 810 default: 811 cdpath = s_new(); 812 vmspath(node, cdpath); 813 } 814 break; 815 case MVS: 816 if(node->depth == 0) 817 cdpath = s_clone(remrootpath); 818 else{ 819 cdpath = s_new(); 820 mvspath(node, cdpath); 821 } 822 break; 823 default: 824 if(node->depth == 0) 825 cdpath = s_clone(remrootpath); 826 else{ 827 cdpath = s_new(); 828 unixpath(node, cdpath); 829 } 830 break; 831 } 832 833 uncachedir(remdir, 0); 834 835 /* 836 * connect, if we need a password (Incomplete) 837 * act like it worked (best we can do). 838 */ 839 sendrequest("CWD", s_to_c(cdpath)); 840 s_free(cdpath); 841 switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 842 case Success: 843 case Incomplete: 844 remdir = node; 845 return 0; 846 default: 847 return seterr(nosuchfile); 848 } 849 } 850 851 /* 852 * read a remote file 853 */ 854 int 855 readfile1(Node *node) 856 { 857 Biobuf *bp; 858 char buf[4*1024]; 859 long off; 860 int n; 861 int tries; 862 863 if(changedir(node->parent) < 0) 864 return -1; 865 866 for(tries = 0; tries < 4; tries++){ 867 switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){ 868 case Extra: 869 break; 870 case TempFail: 871 continue; 872 default: 873 return seterr(nosuchfile); 874 } 875 off = 0; 876 while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){ 877 if(filewrite(node, buf, off, n) != n){ 878 off = -1; 879 break; 880 } 881 off += n; 882 } 883 if(off < 0) 884 return -1; 885 886 /* make sure a file gets created even for a zero length file */ 887 if(off == 0) 888 filewrite(node, buf, 0, 0); 889 890 close(Bfildes(bp)); 891 switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 892 case Success: 893 return off; 894 case TempFail: 895 continue; 896 default: 897 return seterr(nosuchfile); 898 } 899 } 900 return seterr(nosuchfile); 901 } 902 903 int 904 readfile(Node *node) 905 { 906 int rv, inimage; 907 908 switch(os){ 909 case MVS: 910 case Plan9: 911 case Tops: 912 case TSO: 913 inimage = 0; 914 break; 915 default: 916 inimage = 1; 917 image(); 918 break; 919 } 920 921 rv = readfile1(node); 922 923 if(inimage) 924 ascii(); 925 return rv; 926 } 927 928 /* 929 * write back a file 930 */ 931 int 932 createfile1(Node *node) 933 { 934 Biobuf *bp; 935 char buf[4*1024]; 936 long off; 937 int n; 938 939 if(changedir(node->parent) < 0) 940 return -1; 941 942 if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra) 943 return -1; 944 for(off = 0; ; off += n){ 945 n = fileread(node, buf, off, sizeof(buf)); 946 if(n <= 0) 947 break; 948 write(Bfildes(bp), buf, n); 949 } 950 close(Bfildes(bp)); 951 getreply(&ctlin, msg, sizeof(msg), 0); 952 return off; 953 } 954 955 int 956 createfile(Node *node) 957 { 958 int rv; 959 960 switch(os){ 961 case Plan9: 962 case Tops: 963 break; 964 default: 965 image(); 966 break; 967 } 968 rv = createfile1(node); 969 switch(os){ 970 case Plan9: 971 case Tops: 972 break; 973 default: 974 ascii(); 975 break; 976 } 977 return rv; 978 } 979 980 /* 981 * remove a remote file 982 */ 983 int 984 removefile(Node *node) 985 { 986 if(changedir(node->parent) < 0) 987 return -1; 988 989 sendrequest("DELE", s_to_c(node->remname)); 990 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 991 return -1; 992 return 0; 993 } 994 995 /* 996 * remove a remote directory 997 */ 998 int 999 removedir(Node *node) 1000 { 1001 if(changedir(node->parent) < 0) 1002 return -1; 1003 1004 sendrequest("RMD", s_to_c(node->remname)); 1005 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 1006 return -1; 1007 return 0; 1008 } 1009 1010 /* 1011 * tell remote that we're exiting and then do it 1012 */ 1013 void 1014 quit(void) 1015 { 1016 sendrequest("QUIT", nil); 1017 getreply(&ctlin, msg, sizeof(msg), 0); 1018 exits(0); 1019 } 1020 1021 /* 1022 * send a request 1023 */ 1024 static void 1025 sendrequest(char *a, char *b) 1026 { 1027 char buf[2*1024]; 1028 int n; 1029 1030 n = strlen(a)+2+1; 1031 if(b != nil) 1032 n += strlen(b)+1; 1033 if(n >= sizeof(buf)) 1034 fatal("proto request too long"); 1035 strcpy(buf, a); 1036 if(b != nil){ 1037 strcat(buf, " "); 1038 strcat(buf, b); 1039 } 1040 strcat(buf, "\r\n"); 1041 n = strlen(buf); 1042 if(write(ctlfd, buf, n) != n) 1043 fatal("remote side hung up"); 1044 if(debug) 1045 write(2, buf, n); 1046 lastsend = time(0); 1047 } 1048 1049 /* 1050 * replies codes are in the range [100, 999] and may contain multiple lines of 1051 * continuation. 1052 */ 1053 static int 1054 getreply(Biobuf *bp, char *msg, int len, int printreply) 1055 { 1056 char *line; 1057 int rv; 1058 int i, n; 1059 1060 while(line = Brdline(bp, '\n')){ 1061 /* add line to message buffer, strip off \r */ 1062 n = Blinelen(bp); 1063 if(n > 1 && line[n-2] == '\r'){ 1064 n--; 1065 line[n-1] = '\n'; 1066 } 1067 if(printreply && !quiet) 1068 write(1, line, n); 1069 else if(debug) 1070 write(2, line, n); 1071 if(n > len - 1) 1072 i = len - 1; 1073 else 1074 i = n; 1075 if(i > 0){ 1076 memmove(msg, line, i); 1077 msg += i; 1078 len -= i; 1079 *msg = 0; 1080 } 1081 1082 /* stop if not a continuation */ 1083 rv = atoi(line); 1084 if(rv >= 100 && rv < 600 && (n == 4 || (n > 4 && line[3] == ' '))) 1085 return rv/100; 1086 1087 /* tell user about continuations */ 1088 if(!debug && !quiet && !printreply) 1089 write(2, line, n); 1090 } 1091 1092 fatal("remote side closed connection"); 1093 return 0; 1094 } 1095 1096 /* 1097 * Announce on a local port and tell its address to the the remote side 1098 */ 1099 static int 1100 port(void) 1101 { 1102 char buf[256]; 1103 int n, fd; 1104 char *ptr; 1105 uchar ipaddr[IPaddrlen]; 1106 int port; 1107 1108 /* get a channel to listen on, let kernel pick the port number */ 1109 sprint(buf, "%s!*!0", net); 1110 listenfd = announce(buf, netdir); 1111 if(listenfd < 0) 1112 return seterr("can't announce"); 1113 1114 /* get the local address and port number */ 1115 sprint(buf, "%s/local", netdir); 1116 fd = open(buf, OREAD); 1117 if(fd < 0) 1118 return seterr("opening %s: %r", buf); 1119 n = read(fd, buf, sizeof(buf)-1); 1120 close(fd); 1121 if(n <= 0) 1122 return seterr("opening %s/local: %r", netdir); 1123 buf[n] = 0; 1124 ptr = strchr(buf, ' '); 1125 if(ptr) 1126 *ptr = 0; 1127 ptr = strchr(buf, '!')+1; 1128 parseip(ipaddr, buf); 1129 port = atoi(ptr); 1130 1131 /* tell remote side */ 1132 sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1], 1133 ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff); 1134 sendrequest(buf, nil); 1135 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 1136 return seterr(msg); 1137 return 0; 1138 } 1139 1140 /* 1141 * have server call back for a data connection 1142 */ 1143 static int 1144 active(int mode, Biobuf **bpp, char *cmda, char *cmdb) 1145 { 1146 int cfd, dfd, rv; 1147 char newdir[Maxpath]; 1148 char datafile[Maxpath + 6]; 1149 1150 if(port() < 0) 1151 return TempFail; 1152 1153 sendrequest(cmda, cmdb); 1154 1155 rv = getreply(&ctlin, msg, sizeof(msg), 0); 1156 if(rv != Extra){ 1157 close(listenfd); 1158 return rv; 1159 } 1160 1161 /* wait for a new call */ 1162 cfd = listen(netdir, newdir); 1163 if(cfd < 0) 1164 fatal("waiting for data connection"); 1165 close(listenfd); 1166 1167 /* open it's data connection and close the control connection */ 1168 sprint(datafile, "%s/data", newdir); 1169 dfd = open(datafile, ORDWR); 1170 close(cfd); 1171 if(dfd < 0) 1172 fatal("opening data connection"); 1173 Binit(&dbuf, dfd, mode); 1174 *bpp = &dbuf; 1175 return Extra; 1176 } 1177 1178 /* 1179 * call out for a data connection 1180 */ 1181 static int 1182 passive(int mode, Biobuf **bpp, char *cmda, char *cmdb) 1183 { 1184 char msg[1024]; 1185 char ds[1024]; 1186 char *f[6]; 1187 char *p; 1188 int x, fd; 1189 1190 if(nopassive) 1191 return Impossible; 1192 1193 sendrequest("PASV", nil); 1194 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){ 1195 nopassive = 1; 1196 return Impossible; 1197 } 1198 1199 /* get address and port number from reply, this is AI */ 1200 p = strchr(msg, '('); 1201 if(p == 0){ 1202 for(p = msg+3; *p; p++) 1203 if(isdigit(*p)) 1204 break; 1205 } else 1206 p++; 1207 if(getfields(p, f, 6, 0, ",") < 6){ 1208 if(debug) 1209 fprint(2, "passive mode protocol botch: %s\n", msg); 1210 werrstr("ftp protocol botch"); 1211 nopassive = 1; 1212 return Impossible; 1213 } 1214 snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net, 1215 f[0], f[1], f[2], f[3], 1216 ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff)); 1217 1218 /* open data connection */ 1219 fd = dial(ds, 0, 0, 0); 1220 if(fd < 0){ 1221 if(debug) 1222 fprint(2, "passive mode connect to %s failed: %r\n", ds); 1223 nopassive = 1; 1224 return TempFail; 1225 } 1226 1227 /* tell remote to send a file */ 1228 sendrequest(cmda, cmdb); 1229 x = getreply(&ctlin, msg, sizeof(msg), 0); 1230 if(x != Extra){ 1231 close(fd); 1232 if(debug) 1233 fprint(2, "passive mode retrieve failed: %s\n", msg); 1234 werrstr(msg); 1235 return x; 1236 } 1237 1238 Binit(&dbuf, fd, mode); 1239 *bpp = &dbuf; 1240 return Extra; 1241 } 1242 1243 static int 1244 data(int mode, Biobuf **bpp, char* cmda, char *cmdb) 1245 { 1246 int x; 1247 1248 x = passive(mode, bpp, cmda, cmdb); 1249 if(x != Impossible) 1250 return x; 1251 return active(mode, bpp, cmda, cmdb); 1252 } 1253 1254 /* 1255 * used for keep alives 1256 */ 1257 void 1258 nop(void) 1259 { 1260 if(lastsend - time(0) < 15) 1261 return; 1262 sendrequest("PWD", nil); 1263 getreply(&ctlin, msg, sizeof(msg), 0); 1264 } 1265 1266 /* 1267 * turn a vms spec into a path 1268 */ 1269 static Node* 1270 vmsextendpath(Node *np, char *name) 1271 { 1272 np = extendpath(np, s_copy(name)); 1273 if(!ISVALID(np)){ 1274 np->d->qid.type = QTDIR; 1275 np->d->atime = time(0); 1276 np->d->mtime = np->d->atime; 1277 strcpy(np->d->uid, "who"); 1278 strcpy(np->d->gid, "cares"); 1279 np->d->mode = DMDIR|0777; 1280 np->d->length = 0; 1281 if(changedir(np) >= 0) 1282 VALID(np); 1283 } 1284 return np; 1285 } 1286 static Node* 1287 vmsdir(char *name) 1288 { 1289 char *cp; 1290 Node *np; 1291 char *oname; 1292 1293 np = remroot; 1294 cp = strchr(name, '['); 1295 if(cp) 1296 strcpy(cp, cp+1); 1297 cp = strchr(name, ']'); 1298 if(cp) 1299 *cp = 0; 1300 oname = name = strdup(name); 1301 if(name == 0) 1302 return 0; 1303 1304 while(cp = strchr(name, '.')){ 1305 *cp = 0; 1306 np = vmsextendpath(np, name); 1307 name = cp+1; 1308 } 1309 np = vmsextendpath(np, name); 1310 1311 /* 1312 * walk back to first accessible directory 1313 */ 1314 for(; np->parent != np; np = np->parent) 1315 if(ISVALID(np)){ 1316 CACHED(np->parent); 1317 break; 1318 } 1319 1320 free(oname); 1321 return np; 1322 } 1323 1324 /* 1325 * walk up the tree building a VMS style path 1326 */ 1327 static void 1328 vmspath(Node *node, String *path) 1329 { 1330 char *p; 1331 int n; 1332 1333 if(node->depth == 1){ 1334 p = strchr(s_to_c(node->remname), ':'); 1335 if(p){ 1336 n = p - s_to_c(node->remname) + 1; 1337 s_nappend(path, s_to_c(node->remname), n); 1338 s_append(path, "["); 1339 s_append(path, p+1); 1340 } else { 1341 s_append(path, "["); 1342 s_append(path, s_to_c(node->remname)); 1343 } 1344 s_append(path, "]"); 1345 return; 1346 } 1347 vmspath(node->parent, path); 1348 s_append(path, "."); 1349 s_append(path, s_to_c(node->remname)); 1350 } 1351 1352 /* 1353 * walk up the tree building a Unix style path 1354 */ 1355 static void 1356 unixpath(Node *node, String *path) 1357 { 1358 if(node == node->parent){ 1359 s_append(path, s_to_c(remrootpath)); 1360 return; 1361 } 1362 unixpath(node->parent, path); 1363 if(s_len(path) > 0) 1364 s_append(path, "/"); 1365 s_append(path, s_to_c(node->remname)); 1366 } 1367 1368 /* 1369 * walk up the tree building a MVS style path 1370 */ 1371 static void 1372 mvspath(Node *node, String *path) 1373 { 1374 if(node == node->parent){ 1375 s_append(path, s_to_c(remrootpath)); 1376 return; 1377 } 1378 mvspath(node->parent, path); 1379 if(s_len(path) > 0) 1380 s_append(path, "."); 1381 s_append(path, s_to_c(node->remname)); 1382 } 1383 1384 static int 1385 getpassword(char *buf, char *e) 1386 { 1387 char *p; 1388 int c; 1389 int consctl, rv = 0; 1390 1391 consctl = open("/dev/consctl", OWRITE); 1392 if(consctl >= 0) 1393 write(consctl, "rawon", 5); 1394 print("Password: "); 1395 e--; 1396 for(p = buf; p <= e; p++){ 1397 c = Bgetc(&stdin); 1398 if(c < 0){ 1399 rv = -1; 1400 goto out; 1401 } 1402 if(c == '\n' || c == '\r') 1403 break; 1404 *p = c; 1405 } 1406 *p = 0; 1407 print("\n"); 1408 1409 out: 1410 if(consctl >= 0) 1411 close(consctl); 1412 return rv; 1413 } 1414 1415 /* 1416 * convert from latin1 to utf 1417 */ 1418 static char* 1419 fromlatin1(char *from) 1420 { 1421 char *p, *to; 1422 Rune r; 1423 1424 if(os == Plan9) 1425 return nil; 1426 1427 /* don't convert if we don't have to */ 1428 for(p = from; *p; p++) 1429 if(*p & 0x80) 1430 break; 1431 if(*p == 0) 1432 return nil; 1433 1434 to = malloc(3*strlen(from)+2); 1435 if(to == nil) 1436 return nil; 1437 for(p = to; *from; from++){ 1438 r = (*from) & 0xff; 1439 p += runetochar(p, &r); 1440 } 1441 *p = 0; 1442 return to; 1443 } 1444 1445 Dir* 1446 reallocdir(Dir *d, int dofree) 1447 { 1448 Dir *dp; 1449 char *p; 1450 int nn, ng, nu, nm; 1451 char *utf; 1452 1453 if(d->name == nil) 1454 d->name = "?name?"; 1455 if(d->uid == nil) 1456 d->uid = "?uid?"; 1457 if(d->gid == nil) 1458 d->gid = d->uid; 1459 if(d->muid == nil) 1460 d->muid = d->uid; 1461 1462 utf = fromlatin1(d->name); 1463 if(utf != nil) 1464 d->name = utf; 1465 1466 nn = strlen(d->name)+1; 1467 nu = strlen(d->uid)+1; 1468 ng = strlen(d->gid)+1; 1469 nm = strlen(d->muid)+1; 1470 dp = malloc(sizeof(Dir)+nn+nu+ng+nm); 1471 if(dp == nil){ 1472 if(dofree) 1473 free(d); 1474 if(utf != nil) 1475 free(utf); 1476 return nil; 1477 } 1478 *dp = *d; 1479 p = (char*)&dp[1]; 1480 strcpy(p, d->name); 1481 dp->name = p; 1482 p += nn; 1483 strcpy(p, d->uid); 1484 dp->uid = p; 1485 p += nu; 1486 strcpy(p, d->gid); 1487 dp->gid = p; 1488 p += ng; 1489 strcpy(p, d->muid); 1490 dp->muid = p; 1491 if(dofree) 1492 free(d); 1493 if(utf != nil) 1494 free(utf); 1495 return dp; 1496 } 1497 1498 Dir* 1499 dir_change_name(Dir *d, char *name) 1500 { 1501 if(d->name && strlen(d->name) >= strlen(name)){ 1502 strcpy(d->name, name); 1503 return d; 1504 } 1505 d->name = name; 1506 return reallocdir(d, 1); 1507 } 1508 1509 Dir* 1510 dir_change_uid(Dir *d, char *name) 1511 { 1512 if(d->uid && strlen(d->uid) >= strlen(name)){ 1513 strcpy(d->name, name); 1514 return d; 1515 } 1516 d->uid = name; 1517 return reallocdir(d, 1); 1518 } 1519 1520 Dir* 1521 dir_change_gid(Dir *d, char *name) 1522 { 1523 if(d->gid && strlen(d->gid) >= strlen(name)){ 1524 strcpy(d->name, name); 1525 return d; 1526 } 1527 d->gid = name; 1528 return reallocdir(d, 1); 1529 } 1530 1531 Dir* 1532 dir_change_muid(Dir *d, char *name) 1533 { 1534 if(d->muid && strlen(d->muid) >= strlen(name)){ 1535 strcpy(d->name, name); 1536 return d; 1537 } 1538 d->muid = name; 1539 return reallocdir(d, 1); 1540 } 1541