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 getreply(&ctlin, msg, sizeof(msg), 0); 992 return off; 993 } 994 995 int 996 createfile(Node *node) 997 { 998 int rv; 999 1000 switch(os){ 1001 case Plan9: 1002 case Tops: 1003 break; 1004 default: 1005 image(); 1006 break; 1007 } 1008 rv = createfile1(node); 1009 switch(os){ 1010 case Plan9: 1011 case Tops: 1012 break; 1013 default: 1014 ascii(); 1015 break; 1016 } 1017 return rv; 1018 } 1019 1020 /* 1021 * remove a remote file 1022 */ 1023 int 1024 removefile(Node *node) 1025 { 1026 if(changedir(node->parent) < 0) 1027 return -1; 1028 1029 sendrequest("DELE", s_to_c(node->remname)); 1030 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 1031 return -1; 1032 return 0; 1033 } 1034 1035 /* 1036 * remove a remote directory 1037 */ 1038 int 1039 removedir(Node *node) 1040 { 1041 if(changedir(node->parent) < 0) 1042 return -1; 1043 1044 sendrequest("RMD", s_to_c(node->remname)); 1045 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 1046 return -1; 1047 return 0; 1048 } 1049 1050 /* 1051 * tell remote that we're exiting and then do it 1052 */ 1053 void 1054 quit(void) 1055 { 1056 sendrequest("QUIT", nil); 1057 getreply(&ctlin, msg, sizeof(msg), 0); 1058 exits(0); 1059 } 1060 1061 /* 1062 * send a request 1063 */ 1064 static void 1065 sendrequest(char *a, char *b) 1066 { 1067 char buf[2*1024]; 1068 int n; 1069 1070 n = strlen(a)+2+1; 1071 if(b != nil) 1072 n += strlen(b)+1; 1073 if(n >= sizeof(buf)) 1074 fatal("proto request too long"); 1075 strcpy(buf, a); 1076 if(b != nil){ 1077 strcat(buf, " "); 1078 strcat(buf, b); 1079 } 1080 strcat(buf, "\r\n"); 1081 n = strlen(buf); 1082 if(write(ctlfd, buf, n) != n) 1083 fatal("remote side hung up"); 1084 if(debug) 1085 write(2, buf, n); 1086 lastsend = time(0); 1087 } 1088 1089 /* 1090 * replies codes are in the range [100, 999] and may contain multiple lines of 1091 * continuation. 1092 */ 1093 static int 1094 getreply(Biobuf *bp, char *msg, int len, int printreply) 1095 { 1096 char *line; 1097 char *p; 1098 int rv; 1099 int i, n; 1100 1101 while(line = Brdline(bp, '\n')){ 1102 /* add line to message buffer, strip off \r */ 1103 n = Blinelen(bp); 1104 if(n > 1 && line[n-2] == '\r'){ 1105 n--; 1106 line[n-1] = '\n'; 1107 } 1108 if(printreply && !quiet) 1109 write(1, line, n); 1110 else if(debug) 1111 write(2, line, n); 1112 if(n > len - 1) 1113 i = len - 1; 1114 else 1115 i = n; 1116 if(i > 0){ 1117 memmove(msg, line, i); 1118 msg += i; 1119 len -= i; 1120 *msg = 0; 1121 } 1122 1123 /* stop if not a continuation */ 1124 rv = strtol(line, &p, 10); 1125 if(rv >= 100 && rv < 600 && p==line+3 && *p != '-') 1126 return rv/100; 1127 1128 /* tell user about continuations */ 1129 if(!debug && !quiet && !printreply) 1130 write(2, line, n); 1131 } 1132 1133 fatal("remote side closed connection"); 1134 return 0; 1135 } 1136 1137 /* 1138 * Announce on a local port and tell its address to the the remote side 1139 */ 1140 static int 1141 port(void) 1142 { 1143 char buf[256]; 1144 int n, fd; 1145 char *ptr; 1146 uchar ipaddr[IPaddrlen]; 1147 int port; 1148 1149 /* get a channel to listen on, let kernel pick the port number */ 1150 sprint(buf, "%s!*!0", net); 1151 listenfd = announce(buf, netdir); 1152 if(listenfd < 0) 1153 return seterr("can't announce"); 1154 1155 /* get the local address and port number */ 1156 sprint(buf, "%s/local", netdir); 1157 fd = open(buf, OREAD); 1158 if(fd < 0) 1159 return seterr("opening %s: %r", buf); 1160 n = read(fd, buf, sizeof(buf)-1); 1161 close(fd); 1162 if(n <= 0) 1163 return seterr("opening %s/local: %r", netdir); 1164 buf[n] = 0; 1165 ptr = strchr(buf, ' '); 1166 if(ptr) 1167 *ptr = 0; 1168 ptr = strchr(buf, '!')+1; 1169 parseip(ipaddr, buf); 1170 port = atoi(ptr); 1171 1172 /* tell remote side */ 1173 sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1], 1174 ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff); 1175 sendrequest(buf, nil); 1176 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 1177 return seterr(msg); 1178 return 0; 1179 } 1180 1181 /* 1182 * have server call back for a data connection 1183 */ 1184 static int 1185 active(int mode, Biobuf **bpp, char *cmda, char *cmdb) 1186 { 1187 int cfd, dfd, rv; 1188 char newdir[Maxpath]; 1189 char datafile[Maxpath + 6]; 1190 1191 if(port() < 0) 1192 return TempFail; 1193 1194 sendrequest(cmda, cmdb); 1195 1196 rv = getreply(&ctlin, msg, sizeof(msg), 0); 1197 if(rv != Extra){ 1198 close(listenfd); 1199 return rv; 1200 } 1201 1202 /* wait for a new call */ 1203 cfd = listen(netdir, newdir); 1204 if(cfd < 0) 1205 fatal("waiting for data connection"); 1206 close(listenfd); 1207 1208 /* open it's data connection and close the control connection */ 1209 sprint(datafile, "%s/data", newdir); 1210 dfd = open(datafile, ORDWR); 1211 close(cfd); 1212 if(dfd < 0) 1213 fatal("opening data connection"); 1214 Binit(&dbuf, dfd, mode); 1215 *bpp = &dbuf; 1216 return Extra; 1217 } 1218 1219 /* 1220 * call out for a data connection 1221 */ 1222 static int 1223 passive(int mode, Biobuf **bpp, char *cmda, char *cmdb) 1224 { 1225 char msg[1024]; 1226 char ds[1024]; 1227 char *f[6]; 1228 char *p; 1229 int x, fd; 1230 1231 if(nopassive) 1232 return Impossible; 1233 1234 sendrequest("PASV", nil); 1235 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){ 1236 nopassive = 1; 1237 return Impossible; 1238 } 1239 1240 /* get address and port number from reply, this is AI */ 1241 p = strchr(msg, '('); 1242 if(p == 0){ 1243 for(p = msg+3; *p; p++) 1244 if(isdigit(*p)) 1245 break; 1246 } else 1247 p++; 1248 if(getfields(p, f, 6, 0, ",") < 6){ 1249 if(debug) 1250 fprint(2, "passive mode protocol botch: %s\n", msg); 1251 werrstr("ftp protocol botch"); 1252 nopassive = 1; 1253 return Impossible; 1254 } 1255 snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net, 1256 f[0], f[1], f[2], f[3], 1257 ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff)); 1258 1259 /* open data connection */ 1260 fd = dial(ds, 0, 0, 0); 1261 if(fd < 0){ 1262 if(debug) 1263 fprint(2, "passive mode connect to %s failed: %r\n", ds); 1264 nopassive = 1; 1265 return TempFail; 1266 } 1267 1268 /* tell remote to send a file */ 1269 sendrequest(cmda, cmdb); 1270 x = getreply(&ctlin, msg, sizeof(msg), 0); 1271 if(x != Extra){ 1272 close(fd); 1273 if(debug) 1274 fprint(2, "passive mode retrieve failed: %s\n", msg); 1275 werrstr(msg); 1276 return x; 1277 } 1278 1279 Binit(&dbuf, fd, mode); 1280 *bpp = &dbuf; 1281 return Extra; 1282 } 1283 1284 static int 1285 data(int mode, Biobuf **bpp, char* cmda, char *cmdb) 1286 { 1287 int x; 1288 1289 x = passive(mode, bpp, cmda, cmdb); 1290 if(x != Impossible) 1291 return x; 1292 return active(mode, bpp, cmda, cmdb); 1293 } 1294 1295 /* 1296 * used for keep alives 1297 */ 1298 void 1299 nop(void) 1300 { 1301 if(lastsend - time(0) < 15) 1302 return; 1303 sendrequest("PWD", nil); 1304 getreply(&ctlin, msg, sizeof(msg), 0); 1305 } 1306 1307 /* 1308 * turn a vms spec into a path 1309 */ 1310 static Node* 1311 vmsextendpath(Node *np, char *name) 1312 { 1313 np = extendpath(np, s_copy(name)); 1314 if(!ISVALID(np)){ 1315 np->d->qid.type = QTDIR; 1316 np->d->atime = time(0); 1317 np->d->mtime = np->d->atime; 1318 strcpy(np->d->uid, "who"); 1319 strcpy(np->d->gid, "cares"); 1320 np->d->mode = DMDIR|0777; 1321 np->d->length = 0; 1322 if(changedir(np) >= 0) 1323 VALID(np); 1324 } 1325 return np; 1326 } 1327 static Node* 1328 vmsdir(char *name) 1329 { 1330 char *cp; 1331 Node *np; 1332 char *oname; 1333 1334 np = remroot; 1335 cp = strchr(name, '['); 1336 if(cp) 1337 strcpy(cp, cp+1); 1338 cp = strchr(name, ']'); 1339 if(cp) 1340 *cp = 0; 1341 oname = name = strdup(name); 1342 if(name == 0) 1343 return 0; 1344 1345 while(cp = strchr(name, '.')){ 1346 *cp = 0; 1347 np = vmsextendpath(np, name); 1348 name = cp+1; 1349 } 1350 np = vmsextendpath(np, name); 1351 1352 /* 1353 * walk back to first accessible directory 1354 */ 1355 for(; np->parent != np; np = np->parent) 1356 if(ISVALID(np)){ 1357 CACHED(np->parent); 1358 break; 1359 } 1360 1361 free(oname); 1362 return np; 1363 } 1364 1365 /* 1366 * walk up the tree building a VMS style path 1367 */ 1368 static void 1369 vmspath(Node *node, String *path) 1370 { 1371 char *p; 1372 int n; 1373 1374 if(node->depth == 1){ 1375 p = strchr(s_to_c(node->remname), ':'); 1376 if(p){ 1377 n = p - s_to_c(node->remname) + 1; 1378 s_nappend(path, s_to_c(node->remname), n); 1379 s_append(path, "["); 1380 s_append(path, p+1); 1381 } else { 1382 s_append(path, "["); 1383 s_append(path, s_to_c(node->remname)); 1384 } 1385 s_append(path, "]"); 1386 return; 1387 } 1388 vmspath(node->parent, path); 1389 s_append(path, "."); 1390 s_append(path, s_to_c(node->remname)); 1391 } 1392 1393 /* 1394 * walk up the tree building a Unix style path 1395 */ 1396 static void 1397 unixpath(Node *node, String *path) 1398 { 1399 if(node == node->parent){ 1400 s_append(path, s_to_c(remrootpath)); 1401 return; 1402 } 1403 unixpath(node->parent, path); 1404 if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0) 1405 s_append(path, "/"); 1406 s_append(path, s_to_c(node->remname)); 1407 } 1408 1409 /* 1410 * walk up the tree building a MVS style path 1411 */ 1412 static void 1413 mvspath(Node *node, String *path) 1414 { 1415 if(node == node->parent){ 1416 s_append(path, s_to_c(remrootpath)); 1417 return; 1418 } 1419 mvspath(node->parent, path); 1420 if(s_len(path) > 0) 1421 s_append(path, "."); 1422 s_append(path, s_to_c(node->remname)); 1423 } 1424 1425 static int 1426 getpassword(char *buf, char *e) 1427 { 1428 char *p; 1429 int c; 1430 int consctl, rv = 0; 1431 1432 consctl = open("/dev/consctl", OWRITE); 1433 if(consctl >= 0) 1434 write(consctl, "rawon", 5); 1435 print("Password: "); 1436 e--; 1437 for(p = buf; p <= e; p++){ 1438 c = Bgetc(&stdin); 1439 if(c < 0){ 1440 rv = -1; 1441 goto out; 1442 } 1443 if(c == '\n' || c == '\r') 1444 break; 1445 *p = c; 1446 } 1447 *p = 0; 1448 print("\n"); 1449 1450 out: 1451 if(consctl >= 0) 1452 close(consctl); 1453 return rv; 1454 } 1455 1456 /* 1457 * convert from latin1 to utf 1458 */ 1459 static char* 1460 fromlatin1(char *from) 1461 { 1462 char *p, *to; 1463 Rune r; 1464 1465 if(os == Plan9) 1466 return nil; 1467 1468 /* don't convert if we don't have to */ 1469 for(p = from; *p; p++) 1470 if(*p & 0x80) 1471 break; 1472 if(*p == 0) 1473 return nil; 1474 1475 to = malloc(3*strlen(from)+2); 1476 if(to == nil) 1477 return nil; 1478 for(p = to; *from; from++){ 1479 r = (*from) & 0xff; 1480 p += runetochar(p, &r); 1481 } 1482 *p = 0; 1483 return to; 1484 } 1485 1486 Dir* 1487 reallocdir(Dir *d, int dofree) 1488 { 1489 Dir *dp; 1490 char *p; 1491 int nn, ng, nu, nm; 1492 char *utf; 1493 1494 if(d->name == nil) 1495 d->name = "?name?"; 1496 if(d->uid == nil) 1497 d->uid = "?uid?"; 1498 if(d->gid == nil) 1499 d->gid = d->uid; 1500 if(d->muid == nil) 1501 d->muid = d->uid; 1502 1503 utf = fromlatin1(d->name); 1504 if(utf != nil) 1505 d->name = utf; 1506 1507 nn = strlen(d->name)+1; 1508 nu = strlen(d->uid)+1; 1509 ng = strlen(d->gid)+1; 1510 nm = strlen(d->muid)+1; 1511 dp = malloc(sizeof(Dir)+nn+nu+ng+nm); 1512 if(dp == nil){ 1513 if(dofree) 1514 free(d); 1515 if(utf != nil) 1516 free(utf); 1517 return nil; 1518 } 1519 *dp = *d; 1520 p = (char*)&dp[1]; 1521 strcpy(p, d->name); 1522 dp->name = p; 1523 p += nn; 1524 strcpy(p, d->uid); 1525 dp->uid = p; 1526 p += nu; 1527 strcpy(p, d->gid); 1528 dp->gid = p; 1529 p += ng; 1530 strcpy(p, d->muid); 1531 dp->muid = p; 1532 if(dofree) 1533 free(d); 1534 if(utf != nil) 1535 free(utf); 1536 return dp; 1537 } 1538 1539 Dir* 1540 dir_change_name(Dir *d, char *name) 1541 { 1542 if(d->name && strlen(d->name) >= strlen(name)){ 1543 strcpy(d->name, name); 1544 return d; 1545 } 1546 d->name = name; 1547 return reallocdir(d, 1); 1548 } 1549 1550 Dir* 1551 dir_change_uid(Dir *d, char *name) 1552 { 1553 if(d->uid && strlen(d->uid) >= strlen(name)){ 1554 strcpy(d->name, name); 1555 return d; 1556 } 1557 d->uid = name; 1558 return reallocdir(d, 1); 1559 } 1560 1561 Dir* 1562 dir_change_gid(Dir *d, char *name) 1563 { 1564 if(d->gid && strlen(d->gid) >= strlen(name)){ 1565 strcpy(d->name, name); 1566 return d; 1567 } 1568 d->gid = name; 1569 return reallocdir(d, 1); 1570 } 1571 1572 Dir* 1573 dir_change_muid(Dir *d, char *name) 1574 { 1575 if(d->muid && strlen(d->muid) >= strlen(name)){ 1576 strcpy(d->name, name); 1577 return d; 1578 } 1579 d->muid = name; 1580 return reallocdir(d, 1); 1581 } 1582 1583 static int 1584 nw_mode(char dirlet, char *s) /* NetWare file mode mapping */ 1585 { 1586 int mode = 0777; 1587 1588 if(dirlet == 'd') 1589 mode |= DMDIR; 1590 1591 if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']') 1592 return(mode); 1593 1594 if (s[1] == '-') /* can't read file */ 1595 mode &= ~0444; 1596 if (dirlet == 'd' && s[6] == '-') /* cannot scan dir */ 1597 mode &= ~0444; 1598 if (s[2] == '-') /* can't write file */ 1599 mode &= ~0222; 1600 if (dirlet == 'd' && s[7] == '-' && s[3] == '-') /* cannot create in, or modify dir */ 1601 mode &= ~0222; 1602 1603 return(mode); 1604 } 1605