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