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