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