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