19a747e4fSDavid du Colombier #include <u.h> 29a747e4fSDavid du Colombier #include <libc.h> 39a747e4fSDavid du Colombier #include <bio.h> 49a747e4fSDavid du Colombier #include <ip.h> 59a747e4fSDavid du Colombier #include <auth.h> 69a747e4fSDavid du Colombier #include <fcall.h> 79a747e4fSDavid du Colombier #include <ctype.h> 89a747e4fSDavid du Colombier #include <String.h> 99a747e4fSDavid du Colombier #include "ftpfs.h" 109a747e4fSDavid du Colombier 119a747e4fSDavid du Colombier enum 129a747e4fSDavid du Colombier { 139a747e4fSDavid du Colombier /* return codes */ 149a747e4fSDavid du Colombier Extra= 1, 159a747e4fSDavid du Colombier Success= 2, 169a747e4fSDavid du Colombier Incomplete= 3, 179a747e4fSDavid du Colombier TempFail= 4, 189a747e4fSDavid du Colombier PermFail= 5, 199a747e4fSDavid du Colombier Impossible= 6, 209a747e4fSDavid du Colombier }; 219a747e4fSDavid du Colombier 229a747e4fSDavid du Colombier Node *remdir; /* current directory on remote machine */ 239a747e4fSDavid du Colombier Node *remroot; /* root directory on remote machine */ 249a747e4fSDavid du Colombier 259a747e4fSDavid du Colombier int ctlfd; /* fd for control connection */ 269a747e4fSDavid du Colombier Biobuf ctlin; /* input buffer for control connection */ 279a747e4fSDavid du Colombier Biobuf stdin; /* input buffer for standard input */ 289a747e4fSDavid du Colombier Biobuf dbuf; /* buffer for data connection */ 299a747e4fSDavid du Colombier char msg[512]; /* buffer for replies */ 309a747e4fSDavid du Colombier char net[Maxpath]; /* network for connections */ 319a747e4fSDavid du Colombier int listenfd; /* fd to listen on for connections */ 329a747e4fSDavid du Colombier char netdir[Maxpath]; 339a747e4fSDavid du Colombier int os, defos; 349a747e4fSDavid du Colombier char topsdir[64]; /* name of listed directory for TOPS */ 359a747e4fSDavid du Colombier String *remrootpath; /* path on remote side to remote root */ 369a747e4fSDavid du Colombier char *user; 379a747e4fSDavid du Colombier int nopassive; 389a747e4fSDavid du Colombier long lastsend; 399a747e4fSDavid du Colombier 409a747e4fSDavid du Colombier static void sendrequest(char*, char*); 419a747e4fSDavid du Colombier static int getreply(Biobuf*, char*, int, int); 429a747e4fSDavid du Colombier static int active(int, Biobuf**, char*, char*); 439a747e4fSDavid du Colombier static int passive(int, Biobuf**, char*, char*); 449a747e4fSDavid du Colombier static int data(int, Biobuf**, char*, char*); 459a747e4fSDavid du Colombier static int port(void); 469a747e4fSDavid du Colombier static void ascii(void); 479a747e4fSDavid du Colombier static void image(void); 489a747e4fSDavid du Colombier static void unixpath(Node*, String*); 499a747e4fSDavid du Colombier static void vmspath(Node*, String*); 509a747e4fSDavid du Colombier static void mvspath(Node*, String*); 519a747e4fSDavid du Colombier static Node* vmsdir(char*); 529a747e4fSDavid du Colombier static int getpassword(char*, char*); 53ecc2a59cSDavid du Colombier static int nw_mode(char dirlet, char *s); 549a747e4fSDavid du Colombier 559a747e4fSDavid du Colombier /* 569a747e4fSDavid du Colombier * connect to remote server, default network is "tcp/ip" 579a747e4fSDavid du Colombier */ 589a747e4fSDavid du Colombier void 599a747e4fSDavid du Colombier hello(char *dest) 609a747e4fSDavid du Colombier { 619a747e4fSDavid du Colombier char *p; 629a747e4fSDavid du Colombier char dir[Maxpath]; 639a747e4fSDavid du Colombier 649a747e4fSDavid du Colombier Binit(&stdin, 0, OREAD); /* init for later use */ 659a747e4fSDavid du Colombier 669a747e4fSDavid du Colombier ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0); 679a747e4fSDavid du Colombier if(ctlfd < 0){ 689a747e4fSDavid du Colombier fprint(2, "can't dial %s: %r\n", dest); 699a747e4fSDavid du Colombier exits("dialing"); 709a747e4fSDavid du Colombier } 719a747e4fSDavid du Colombier Binit(&ctlin, ctlfd, OREAD); 729a747e4fSDavid du Colombier 739a747e4fSDavid du Colombier /* remember network for the data connections */ 749a747e4fSDavid du Colombier p = strrchr(dir+1, '/'); 759a747e4fSDavid du Colombier if(p == 0) 769a747e4fSDavid du Colombier fatal("wrong dial(2) linked with ftp"); 779a747e4fSDavid du Colombier *p = 0; 789a747e4fSDavid du Colombier safecpy(net, dir, sizeof(net)); 799a747e4fSDavid du Colombier 809a747e4fSDavid du Colombier /* wait for hello from other side */ 819a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 1) != Success) 829a747e4fSDavid du Colombier fatal("bad hello"); 83d9306527SDavid du Colombier if(strstr(msg, "Plan 9")) 84d9306527SDavid du Colombier os = Plan9; 859a747e4fSDavid du Colombier } 869a747e4fSDavid du Colombier 879a747e4fSDavid du Colombier /* 889a747e4fSDavid du Colombier * login to remote system 899a747e4fSDavid du Colombier */ 909a747e4fSDavid du Colombier void 91eaa278a2SDavid du Colombier rlogin(char *rsys, char *keyspec) 929a747e4fSDavid du Colombier { 939a747e4fSDavid du Colombier char *line; 949a747e4fSDavid du Colombier char pass[128]; 95d9306527SDavid du Colombier UserPasswd *up; 969a747e4fSDavid du Colombier 97d9306527SDavid du Colombier up = nil; 989a747e4fSDavid du Colombier for(;;){ 99d9306527SDavid du Colombier if(up == nil && os != Plan9) 100eaa278a2SDavid du Colombier up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec); 101d9306527SDavid du Colombier if(up != nil){ 102d9306527SDavid du Colombier sendrequest("USER", up->user); 103d9306527SDavid du Colombier } else { 1049a747e4fSDavid du Colombier print("User[default = %s]: ", user); 1059a747e4fSDavid du Colombier line = Brdline(&stdin, '\n'); 1069a747e4fSDavid du Colombier if(line == 0) 1079a747e4fSDavid du Colombier exits(0); 1089a747e4fSDavid du Colombier line[Blinelen(&stdin)-1] = 0; 1099a747e4fSDavid du Colombier if(*line){ 1109a747e4fSDavid du Colombier free(user); 1119a747e4fSDavid du Colombier user = strdup(line); 1129a747e4fSDavid du Colombier } 1139a747e4fSDavid du Colombier sendrequest("USER", user); 114d9306527SDavid du Colombier } 1159a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 1)){ 1169a747e4fSDavid du Colombier case Success: 117d9306527SDavid du Colombier goto out; 1189a747e4fSDavid du Colombier case Incomplete: 1199a747e4fSDavid du Colombier break; 1209a747e4fSDavid du Colombier case TempFail: 1219a747e4fSDavid du Colombier case PermFail: 1229a747e4fSDavid du Colombier continue; 1239a747e4fSDavid du Colombier } 1249a747e4fSDavid du Colombier 125d9306527SDavid du Colombier if(up != nil){ 126d9306527SDavid du Colombier sendrequest("PASS", up->passwd); 127d9306527SDavid du Colombier } else { 1289a747e4fSDavid du Colombier if(getpassword(pass, pass+sizeof(pass)) < 0) 1299a747e4fSDavid du Colombier exits(0); 1309a747e4fSDavid du Colombier sendrequest("PASS", pass); 131d9306527SDavid du Colombier } 1329a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){ 1339a747e4fSDavid du Colombier if(strstr(msg, "Sess#")) 1349a747e4fSDavid du Colombier defos = MVS; 135d9306527SDavid du Colombier break; 1369a747e4fSDavid du Colombier } 1379a747e4fSDavid du Colombier } 138d9306527SDavid du Colombier out: 139d9306527SDavid du Colombier if(up != nil){ 140d9306527SDavid du Colombier memset(up, 0, sizeof(*up)); 141d9306527SDavid du Colombier free(up); 142d9306527SDavid du Colombier } 1439a747e4fSDavid du Colombier } 1449a747e4fSDavid du Colombier 1459a747e4fSDavid du Colombier /* 1469a747e4fSDavid du Colombier * login to remote system with given user name and password. 1479a747e4fSDavid du Colombier */ 1489a747e4fSDavid du Colombier void 1499a747e4fSDavid du Colombier clogin(char *cuser, char *cpassword) 1509a747e4fSDavid du Colombier { 1519a747e4fSDavid du Colombier free(user); 1529a747e4fSDavid du Colombier user = strdup(cuser); 1539a747e4fSDavid du Colombier if (strcmp(user, "anonymous") != 0 && 1549a747e4fSDavid du Colombier strcmp(user, "ftp") != 0) 1559a747e4fSDavid du Colombier fatal("User must be 'anonymous' or 'ftp'"); 1569a747e4fSDavid du Colombier sendrequest("USER", user); 1579a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 1)){ 1589a747e4fSDavid du Colombier case Success: 1599a747e4fSDavid du Colombier return; 1609a747e4fSDavid du Colombier case Incomplete: 1619a747e4fSDavid du Colombier break; 1629a747e4fSDavid du Colombier case TempFail: 1639a747e4fSDavid du Colombier case PermFail: 1649a747e4fSDavid du Colombier fatal("login failed"); 1659a747e4fSDavid du Colombier } 1669a747e4fSDavid du Colombier if (cpassword == 0) 1679a747e4fSDavid du Colombier fatal("password needed"); 1689a747e4fSDavid du Colombier sendrequest("PASS", cpassword); 1699a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 1) != Success) 1709a747e4fSDavid du Colombier fatal("password failed"); 1719a747e4fSDavid du Colombier if(strstr(msg, "Sess#")) 1729a747e4fSDavid du Colombier defos = MVS; 1739a747e4fSDavid du Colombier return; 1749a747e4fSDavid du Colombier } 1759a747e4fSDavid du Colombier 1769a747e4fSDavid du Colombier /* 1779a747e4fSDavid du Colombier * find out about the other side. go to it's root if requested. set 1789a747e4fSDavid du Colombier * image mode if a Plan9 system. 1799a747e4fSDavid du Colombier */ 1809a747e4fSDavid du Colombier void 1819a747e4fSDavid du Colombier preamble(char *mountroot) 1829a747e4fSDavid du Colombier { 1839a747e4fSDavid du Colombier char *p, *ep; 1849a747e4fSDavid du Colombier int rv; 1859a747e4fSDavid du Colombier OS *o; 1869a747e4fSDavid du Colombier 1879a747e4fSDavid du Colombier /* 1889a747e4fSDavid du Colombier * create a root directory mirror 1899a747e4fSDavid du Colombier */ 1909a747e4fSDavid du Colombier remroot = newnode(0, s_copy("/")); 1919a747e4fSDavid du Colombier remroot->d->qid.type = QTDIR; 1929a747e4fSDavid du Colombier remroot->d->mode = DMDIR|0777; 1939a747e4fSDavid du Colombier remdir = remroot; 1949a747e4fSDavid du Colombier 1959a747e4fSDavid du Colombier /* 1969a747e4fSDavid du Colombier * get system type 1979a747e4fSDavid du Colombier */ 1989a747e4fSDavid du Colombier sendrequest("SYST", nil); 1999a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 1)){ 2009a747e4fSDavid du Colombier case Success: 2019a747e4fSDavid du Colombier for(o = oslist; o->os != Unknown; o++) 2029a747e4fSDavid du Colombier if(strncmp(msg+4, o->name, strlen(o->name)) == 0) 2039a747e4fSDavid du Colombier break; 2049a747e4fSDavid du Colombier os = o->os; 2059a747e4fSDavid du Colombier if(os == NT) 2069a747e4fSDavid du Colombier os = Unix; 2079a747e4fSDavid du Colombier break; 2089a747e4fSDavid du Colombier default: 2099a747e4fSDavid du Colombier os = defos; 2109a747e4fSDavid du Colombier break; 2119a747e4fSDavid du Colombier } 2129a747e4fSDavid du Colombier if(os == Unknown) 2139a747e4fSDavid du Colombier os = defos; 2149a747e4fSDavid du Colombier 2159a747e4fSDavid du Colombier remrootpath = s_reset(remrootpath); 2169a747e4fSDavid du Colombier switch(os){ 2176b6b9ac8SDavid du Colombier case NetWare: 2186b6b9ac8SDavid du Colombier /* 2196b6b9ac8SDavid du Colombier * Request long, rather than 8.3 filenames, 2206b6b9ac8SDavid du Colombier * where the Servers & Volume support them. 2216b6b9ac8SDavid du Colombier */ 2226b6b9ac8SDavid du Colombier sendrequest("SITE LONG", nil); 2236b6b9ac8SDavid du Colombier getreply(&ctlin, msg, sizeof(msg), 0); 2246b6b9ac8SDavid du Colombier /* FALL THRU */ 2259a747e4fSDavid du Colombier case Unix: 2269a747e4fSDavid du Colombier case Plan9: 2279a747e4fSDavid du Colombier /* 2289a747e4fSDavid du Colombier * go to the remote root, if asked 2299a747e4fSDavid du Colombier */ 2309a747e4fSDavid du Colombier if(mountroot){ 2319a747e4fSDavid du Colombier sendrequest("CWD", mountroot); 2329a747e4fSDavid du Colombier getreply(&ctlin, msg, sizeof(msg), 0); 2339a747e4fSDavid du Colombier } else { 2349a747e4fSDavid du Colombier s_append(remrootpath, "/usr/"); 2359a747e4fSDavid du Colombier s_append(remrootpath, user); 2369a747e4fSDavid du Colombier } 2379a747e4fSDavid du Colombier 2389a747e4fSDavid du Colombier /* 2399a747e4fSDavid du Colombier * get the root directory 2409a747e4fSDavid du Colombier */ 2419a747e4fSDavid du Colombier sendrequest("PWD", nil); 2429a747e4fSDavid du Colombier rv = getreply(&ctlin, msg, sizeof(msg), 1); 2439a747e4fSDavid du Colombier if(rv == PermFail){ 2449a747e4fSDavid du Colombier sendrequest("XPWD", nil); 2459a747e4fSDavid du Colombier rv = getreply(&ctlin, msg, sizeof(msg), 1); 2469a747e4fSDavid du Colombier } 2479a747e4fSDavid du Colombier if(rv == Success){ 2489a747e4fSDavid du Colombier p = strchr(msg, '"'); 2499a747e4fSDavid du Colombier if(p){ 2509a747e4fSDavid du Colombier p++; 2519a747e4fSDavid du Colombier ep = strchr(p, '"'); 2529a747e4fSDavid du Colombier if(ep){ 2539a747e4fSDavid du Colombier *ep = 0; 2549a747e4fSDavid du Colombier s_append(s_reset(remrootpath), p); 2559a747e4fSDavid du Colombier } 2569a747e4fSDavid du Colombier } 2579a747e4fSDavid du Colombier } 2589a747e4fSDavid du Colombier 2599a747e4fSDavid du Colombier break; 2609a747e4fSDavid du Colombier case Tops: 2619a747e4fSDavid du Colombier case VM: 2629a747e4fSDavid du Colombier /* 2639a747e4fSDavid du Colombier * top directory is a figment of our imagination. 2649a747e4fSDavid du Colombier * make it permanently cached & valid. 2659a747e4fSDavid du Colombier */ 2669a747e4fSDavid du Colombier CACHED(remroot); 2679a747e4fSDavid du Colombier VALID(remroot); 2689a747e4fSDavid du Colombier remroot->d->atime = time(0) + 100000; 2699a747e4fSDavid du Colombier 2709a747e4fSDavid du Colombier /* 2719a747e4fSDavid du Colombier * no initial directory. We are in the 2729a747e4fSDavid du Colombier * imaginary root. 2739a747e4fSDavid du Colombier */ 2749a747e4fSDavid du Colombier remdir = newtopsdir("???"); 2759a747e4fSDavid du Colombier topsdir[0] = 0; 2769a747e4fSDavid du Colombier if(os == Tops && readdir(remdir) >= 0){ 2779a747e4fSDavid du Colombier CACHED(remdir); 2789a747e4fSDavid du Colombier if(*topsdir) 2799a747e4fSDavid du Colombier remdir->remname = s_copy(topsdir); 2809a747e4fSDavid du Colombier VALID(remdir); 2819a747e4fSDavid du Colombier } 2829a747e4fSDavid du Colombier break; 2839a747e4fSDavid du Colombier case VMS: 2849a747e4fSDavid du Colombier /* 2859a747e4fSDavid du Colombier * top directory is a figment of our imagination. 2869a747e4fSDavid du Colombier * make it permanently cached & valid. 2879a747e4fSDavid du Colombier */ 2889a747e4fSDavid du Colombier CACHED(remroot); 2899a747e4fSDavid du Colombier VALID(remroot); 2909a747e4fSDavid du Colombier remroot->d->atime = time(0) + 100000; 2919a747e4fSDavid du Colombier 2929a747e4fSDavid du Colombier /* 2939a747e4fSDavid du Colombier * get current directory 2949a747e4fSDavid du Colombier */ 2959a747e4fSDavid du Colombier sendrequest("PWD", nil); 2969a747e4fSDavid du Colombier rv = getreply(&ctlin, msg, sizeof(msg), 1); 2979a747e4fSDavid du Colombier if(rv == PermFail){ 2989a747e4fSDavid du Colombier sendrequest("XPWD", nil); 2999a747e4fSDavid du Colombier rv = getreply(&ctlin, msg, sizeof(msg), 1); 3009a747e4fSDavid du Colombier } 3019a747e4fSDavid du Colombier if(rv == Success){ 3029a747e4fSDavid du Colombier p = strchr(msg, '"'); 3039a747e4fSDavid du Colombier if(p){ 3049a747e4fSDavid du Colombier p++; 3059a747e4fSDavid du Colombier ep = strchr(p, '"'); 3069a747e4fSDavid du Colombier if(ep){ 3079a747e4fSDavid du Colombier *ep = 0; 3089a747e4fSDavid du Colombier remroot = remdir = vmsdir(p); 3099a747e4fSDavid du Colombier } 3109a747e4fSDavid du Colombier } 3119a747e4fSDavid du Colombier } 3129a747e4fSDavid du Colombier break; 3139a747e4fSDavid du Colombier case MVS: 3149a747e4fSDavid du Colombier usenlst = 1; 3159a747e4fSDavid du Colombier break; 3169a747e4fSDavid du Colombier } 3179a747e4fSDavid du Colombier 3189a747e4fSDavid du Colombier if(os == Plan9) 3199a747e4fSDavid du Colombier image(); 3209a747e4fSDavid du Colombier } 3219a747e4fSDavid du Colombier 3229a747e4fSDavid du Colombier static void 3239a747e4fSDavid du Colombier ascii(void) 3249a747e4fSDavid du Colombier { 3259a747e4fSDavid du Colombier sendrequest("TYPE A", nil); 3269a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 3279a747e4fSDavid du Colombier case Success: 3289a747e4fSDavid du Colombier break; 3299a747e4fSDavid du Colombier default: 3309a747e4fSDavid du Colombier fatal("can't set type to ascii"); 3319a747e4fSDavid du Colombier } 3329a747e4fSDavid du Colombier } 3339a747e4fSDavid du Colombier 3349a747e4fSDavid du Colombier static void 3359a747e4fSDavid du Colombier image(void) 3369a747e4fSDavid du Colombier { 3379a747e4fSDavid du Colombier sendrequest("TYPE I", nil); 3389a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 3399a747e4fSDavid du Colombier case Success: 3409a747e4fSDavid du Colombier break; 3419a747e4fSDavid du Colombier default: 3429a747e4fSDavid du Colombier fatal("can't set type to image/binary"); 3439a747e4fSDavid du Colombier } 3449a747e4fSDavid du Colombier } 3459a747e4fSDavid du Colombier 3469a747e4fSDavid du Colombier /* 3479a747e4fSDavid du Colombier * decode the time fields, return seconds since epoch began 3489a747e4fSDavid du Colombier */ 3499a747e4fSDavid du Colombier char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec"; 3509a747e4fSDavid du Colombier static Tm now; 3519a747e4fSDavid du Colombier 3529a747e4fSDavid du Colombier static ulong 3539a747e4fSDavid du Colombier cracktime(char *month, char *day, char *yr, char *hms) 3549a747e4fSDavid du Colombier { 3559a747e4fSDavid du Colombier Tm tm; 3569a747e4fSDavid du Colombier int i; 3579a747e4fSDavid du Colombier char *p; 3589a747e4fSDavid du Colombier 3596b6b9ac8SDavid du Colombier 3609a747e4fSDavid du Colombier /* default time */ 3619a747e4fSDavid du Colombier if(now.year == 0) 3629a747e4fSDavid du Colombier now = *localtime(time(0)); 3639a747e4fSDavid du Colombier tm = now; 3646b6b9ac8SDavid du Colombier tm.yday = 0; 3659a747e4fSDavid du Colombier 3669a747e4fSDavid du Colombier /* convert ascii month to a number twixt 1 and 12 */ 3679a747e4fSDavid du Colombier if(*month >= '0' && *month <= '9'){ 3689a747e4fSDavid du Colombier tm.mon = atoi(month) - 1; 3699a747e4fSDavid du Colombier if(tm.mon < 0 || tm.mon > 11) 3709a747e4fSDavid du Colombier tm.mon = 5; 3719a747e4fSDavid du Colombier } else { 3729a747e4fSDavid du Colombier for(p = month; *p; p++) 3739a747e4fSDavid du Colombier *p = tolower(*p); 3749a747e4fSDavid du Colombier for(i = 0; i < 12; i++) 3759a747e4fSDavid du Colombier if(strncmp(&monthchars[i*3], month, 3) == 0){ 3769a747e4fSDavid du Colombier tm.mon = i; 3779a747e4fSDavid du Colombier break; 3789a747e4fSDavid du Colombier } 3799a747e4fSDavid du Colombier } 3809a747e4fSDavid du Colombier 3819a747e4fSDavid du Colombier tm.mday = atoi(day); 3829a747e4fSDavid du Colombier 3839a747e4fSDavid du Colombier if(hms){ 3849a747e4fSDavid du Colombier tm.hour = strtol(hms, &p, 0); 3859a747e4fSDavid du Colombier if(*p == ':'){ 3869a747e4fSDavid du Colombier tm.min = strtol(p+1, &p, 0); 3879a747e4fSDavid du Colombier if(*p == ':') 3889a747e4fSDavid du Colombier tm.sec = strtol(p+1, &p, 0); 3899a747e4fSDavid du Colombier } 3909a747e4fSDavid du Colombier if(tolower(*p) == 'p') 3919a747e4fSDavid du Colombier tm.hour += 12; 3929a747e4fSDavid du Colombier } 3939a747e4fSDavid du Colombier 3949a747e4fSDavid du Colombier if(yr){ 3959a747e4fSDavid du Colombier tm.year = atoi(yr); 3969a747e4fSDavid du Colombier if(tm.year >= 1900) 3979a747e4fSDavid du Colombier tm.year -= 1900; 3989a747e4fSDavid du Colombier } else { 3999a747e4fSDavid du Colombier if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1)) 4009a747e4fSDavid du Colombier tm.year--; 4019a747e4fSDavid du Colombier } 4029a747e4fSDavid du Colombier 4039a747e4fSDavid du Colombier /* convert to epoch seconds */ 4049a747e4fSDavid du Colombier return tm2sec(&tm); 4059a747e4fSDavid du Colombier } 4069a747e4fSDavid du Colombier 4079a747e4fSDavid du Colombier /* 4089a747e4fSDavid du Colombier * decode a Unix or Plan 9 file mode 4099a747e4fSDavid du Colombier */ 4109a747e4fSDavid du Colombier static ulong 4119a747e4fSDavid du Colombier crackmode(char *p) 4129a747e4fSDavid du Colombier { 4139a747e4fSDavid du Colombier ulong flags; 4149a747e4fSDavid du Colombier ulong mode; 4159a747e4fSDavid du Colombier int i; 4169a747e4fSDavid du Colombier 4179a747e4fSDavid du Colombier flags = 0; 4189a747e4fSDavid du Colombier switch(strlen(p)){ 4199a747e4fSDavid du Colombier case 10: /* unix and new style plan 9 */ 4209a747e4fSDavid du Colombier switch(*p){ 4219a747e4fSDavid du Colombier case 'l': 4229a747e4fSDavid du Colombier return DMSYML|0777; 4239a747e4fSDavid du Colombier case 'd': 4249a747e4fSDavid du Colombier flags |= DMDIR; 4259a747e4fSDavid du Colombier case 'a': 4269a747e4fSDavid du Colombier flags |= DMAPPEND; 4279a747e4fSDavid du Colombier } 4289a747e4fSDavid du Colombier p++; 4299a747e4fSDavid du Colombier if(p[2] == 'l') 4309a747e4fSDavid du Colombier flags |= DMEXCL; 4319a747e4fSDavid du Colombier break; 4329a747e4fSDavid du Colombier case 11: /* old style plan 9 */ 4339a747e4fSDavid du Colombier switch(*p++){ 4349a747e4fSDavid du Colombier case 'd': 4359a747e4fSDavid du Colombier flags |= DMDIR; 4369a747e4fSDavid du Colombier break; 4379a747e4fSDavid du Colombier case 'a': 4389a747e4fSDavid du Colombier flags |= DMAPPEND; 4399a747e4fSDavid du Colombier break; 4409a747e4fSDavid du Colombier } 4419a747e4fSDavid du Colombier if(*p++ == 'l') 4429a747e4fSDavid du Colombier flags |= DMEXCL; 4439a747e4fSDavid du Colombier break; 4449a747e4fSDavid du Colombier default: 4459a747e4fSDavid du Colombier return DMDIR|0777; 4469a747e4fSDavid du Colombier } 4479a747e4fSDavid du Colombier mode = 0; 4489a747e4fSDavid du Colombier for(i = 0; i < 3; i++){ 4499a747e4fSDavid du Colombier mode <<= 3; 4509a747e4fSDavid du Colombier if(*p++ == 'r') 4519a747e4fSDavid du Colombier mode |= DMREAD; 4529a747e4fSDavid du Colombier if(*p++ == 'w') 4539a747e4fSDavid du Colombier mode |= DMWRITE; 4549a747e4fSDavid du Colombier if(*p == 'x' || *p == 's' || *p == 'S') 4559a747e4fSDavid du Colombier mode |= DMEXEC; 4569a747e4fSDavid du Colombier p++; 4579a747e4fSDavid du Colombier } 4589a747e4fSDavid du Colombier return mode | flags; 4599a747e4fSDavid du Colombier } 4609a747e4fSDavid du Colombier 4619a747e4fSDavid du Colombier /* 4629a747e4fSDavid du Colombier * find first punctuation 4639a747e4fSDavid du Colombier */ 4649a747e4fSDavid du Colombier char* 4659a747e4fSDavid du Colombier strpunct(char *p) 4669a747e4fSDavid du Colombier { 4679a747e4fSDavid du Colombier int c; 4689a747e4fSDavid du Colombier 4699a747e4fSDavid du Colombier for(;c = *p; p++){ 4709a747e4fSDavid du Colombier if(ispunct(c)) 4719a747e4fSDavid du Colombier return p; 4729a747e4fSDavid du Colombier } 4739a747e4fSDavid du Colombier return 0; 4749a747e4fSDavid du Colombier } 4759a747e4fSDavid du Colombier 4769a747e4fSDavid du Colombier /* 4779a747e4fSDavid du Colombier * decode a Unix or Plan 9 directory listing 4789a747e4fSDavid du Colombier */ 4799a747e4fSDavid du Colombier static Dir* 4809a747e4fSDavid du Colombier crackdir(char *p, String **remname) 4819a747e4fSDavid du Colombier { 4829a747e4fSDavid du Colombier char *field[15]; 4839a747e4fSDavid du Colombier char *dfield[4]; 4849a747e4fSDavid du Colombier char *cp; 4859a747e4fSDavid du Colombier String *s; 4869a747e4fSDavid du Colombier int dn, n; 4879a747e4fSDavid du Colombier Dir d, *dp; 4889a747e4fSDavid du Colombier 4899a747e4fSDavid du Colombier memset(&d, 0, sizeof(d)); 4909a747e4fSDavid du Colombier 4919a747e4fSDavid du Colombier n = getfields(p, field, 15, 1, " \t"); 4929a747e4fSDavid du Colombier if(n > 2 && strcmp(field[n-2], "->") == 0) 4939a747e4fSDavid du Colombier n -= 2; 4949a747e4fSDavid du Colombier switch(os){ 4959a747e4fSDavid du Colombier case TSO: 4969a747e4fSDavid du Colombier cp = strchr(field[0], '.'); 4979a747e4fSDavid du Colombier if(cp){ 4989a747e4fSDavid du Colombier *cp++ = 0; 4999a747e4fSDavid du Colombier s = s_copy(cp); 5009a747e4fSDavid du Colombier d.uid = field[0]; 5019a747e4fSDavid du Colombier } else { 5029a747e4fSDavid du Colombier s = s_copy(field[0]); 5039a747e4fSDavid du Colombier d.uid = "TSO"; 5049a747e4fSDavid du Colombier } 5059a747e4fSDavid du Colombier d.gid = "TSO"; 5069a747e4fSDavid du Colombier d.mode = 0666; 5079a747e4fSDavid du Colombier d.length = 0; 5089a747e4fSDavid du Colombier d.atime = 0; 5099a747e4fSDavid du Colombier break; 5109a747e4fSDavid du Colombier case OS½: 5119a747e4fSDavid du Colombier s = s_copy(field[n-1]); 5129a747e4fSDavid du Colombier d.uid = "OS½"; 5139a747e4fSDavid du Colombier d.gid = d.uid; 5149a747e4fSDavid du Colombier d.mode = 0666; 5159a747e4fSDavid du Colombier switch(n){ 5169a747e4fSDavid du Colombier case 5: 5179a747e4fSDavid du Colombier if(strcmp(field[1], "DIR") == 0) 5189a747e4fSDavid du Colombier d.mode |= DMDIR; 5199a747e4fSDavid du Colombier d.length = atoi(field[0]); 5209a747e4fSDavid du Colombier dn = getfields(field[2], dfield, 4, 1, "-"); 5219a747e4fSDavid du Colombier if(dn == 3) 5229a747e4fSDavid du Colombier d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]); 5239a747e4fSDavid du Colombier break; 5249a747e4fSDavid du Colombier } 5259a747e4fSDavid du Colombier break; 5269a747e4fSDavid du Colombier case Tops: 5279a747e4fSDavid du Colombier if(n != 4){ /* tops directory name */ 5289a747e4fSDavid du Colombier safecpy(topsdir, field[0], sizeof(topsdir)); 5299a747e4fSDavid du Colombier return 0; 5309a747e4fSDavid du Colombier } 5319a747e4fSDavid du Colombier s = s_copy(field[3]); 5329a747e4fSDavid du Colombier d.length = atoi(field[0]); 5339a747e4fSDavid du Colombier d.mode = 0666; 5349a747e4fSDavid du Colombier d.uid = "Tops"; 5359a747e4fSDavid du Colombier d.gid = d.uid; 5369a747e4fSDavid du Colombier dn = getfields(field[1], dfield, 4, 1, "-"); 5379a747e4fSDavid du Colombier if(dn == 3) 5389a747e4fSDavid du Colombier d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]); 5399a747e4fSDavid du Colombier else 5409a747e4fSDavid du Colombier d.atime = time(0); 5419a747e4fSDavid du Colombier break; 5429a747e4fSDavid du Colombier case VM: 5439a747e4fSDavid du Colombier switch(n){ 5449a747e4fSDavid du Colombier case 9: 5459a747e4fSDavid du Colombier s = s_copy(field[0]); 5469a747e4fSDavid du Colombier s_append(s, "."); 5479a747e4fSDavid du Colombier s_append(s, field[1]); 5489a747e4fSDavid du Colombier d.length = atoi(field[3])*atoi(field[4]); 5499a747e4fSDavid du Colombier if(*field[2] == 'F') 5509a747e4fSDavid du Colombier d.mode = 0666; 5519a747e4fSDavid du Colombier else 5529a747e4fSDavid du Colombier d.mode = 0777; 5539a747e4fSDavid du Colombier d.uid = "VM"; 5549a747e4fSDavid du Colombier d.gid = d.uid; 5559a747e4fSDavid du Colombier dn = getfields(field[6], dfield, 4, 1, "/-"); 5569a747e4fSDavid du Colombier if(dn == 3) 5579a747e4fSDavid du Colombier d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]); 5589a747e4fSDavid du Colombier else 5599a747e4fSDavid du Colombier d.atime = time(0); 5609a747e4fSDavid du Colombier break; 5619a747e4fSDavid du Colombier case 1: 5629a747e4fSDavid du Colombier s = s_copy(field[0]); 5639a747e4fSDavid du Colombier d.uid = "VM"; 5649a747e4fSDavid du Colombier d.gid = d.uid; 5659a747e4fSDavid du Colombier d.mode = 0777; 5669a747e4fSDavid du Colombier d.atime = 0; 5679a747e4fSDavid du Colombier break; 5689a747e4fSDavid du Colombier default: 5699a747e4fSDavid du Colombier return nil; 5709a747e4fSDavid du Colombier } 5719a747e4fSDavid du Colombier break; 5729a747e4fSDavid du Colombier case VMS: 5739a747e4fSDavid du Colombier switch(n){ 5749a747e4fSDavid du Colombier case 6: 5759a747e4fSDavid du Colombier for(cp = field[0]; *cp; cp++) 5769a747e4fSDavid du Colombier *cp = tolower(*cp); 5779a747e4fSDavid du Colombier cp = strchr(field[0], ';'); 5789a747e4fSDavid du Colombier if(cp) 5799a747e4fSDavid du Colombier *cp = 0; 5809a747e4fSDavid du Colombier d.mode = 0666; 5819a747e4fSDavid du Colombier cp = field[0] + strlen(field[0]) - 4; 5829a747e4fSDavid du Colombier if(strcmp(cp, ".dir") == 0){ 5839a747e4fSDavid du Colombier d.mode |= DMDIR; 5849a747e4fSDavid du Colombier *cp = 0; 5859a747e4fSDavid du Colombier } 5869a747e4fSDavid du Colombier s = s_copy(field[0]); 5879a747e4fSDavid du Colombier d.length = atoi(field[1])*512; 5889a747e4fSDavid du Colombier field[4][strlen(field[4])-1] = 0; 5899a747e4fSDavid du Colombier d.uid = field[4]+1; 5909a747e4fSDavid du Colombier d.gid = d.uid; 5919a747e4fSDavid du Colombier dn = getfields(field[2], dfield, 4, 1, "/-"); 5929a747e4fSDavid du Colombier if(dn == 3) 5939a747e4fSDavid du Colombier d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]); 5949a747e4fSDavid du Colombier else 5959a747e4fSDavid du Colombier d.atime = time(0); 5969a747e4fSDavid du Colombier break; 5979a747e4fSDavid du Colombier default: 5989a747e4fSDavid du Colombier return nil; 5999a747e4fSDavid du Colombier } 6009a747e4fSDavid du Colombier break; 6019a747e4fSDavid du Colombier case NetWare: 6029a747e4fSDavid du Colombier switch(n){ 603ecc2a59cSDavid du Colombier case 8: /* New style */ 604ecc2a59cSDavid du Colombier s = s_copy(field[7]); 605ecc2a59cSDavid du Colombier d.uid = field[2]; 606ecc2a59cSDavid du Colombier d.gid = d.uid; 607ecc2a59cSDavid du Colombier d.mode = nw_mode(field[0][0], field[1]); 608ecc2a59cSDavid du Colombier d.length = atoi(field[3]); 609ecc2a59cSDavid du Colombier if(strchr(field[6], ':')) 610ecc2a59cSDavid du Colombier d.atime = cracktime(field[4], field[5], nil, field[6]); 611ecc2a59cSDavid du Colombier else 612ecc2a59cSDavid du Colombier d.atime = cracktime(field[4], field[5], field[6], nil); 613ecc2a59cSDavid du Colombier break; 6149a747e4fSDavid du Colombier case 9: 6159a747e4fSDavid du Colombier s = s_copy(field[8]); 6169a747e4fSDavid du Colombier d.uid = field[2]; 6179a747e4fSDavid du Colombier d.gid = d.uid; 6189a747e4fSDavid du Colombier d.mode = 0666; 6199a747e4fSDavid du Colombier if(*field[0] == 'd') 6209a747e4fSDavid du Colombier d.mode |= DMDIR; 6219a747e4fSDavid du Colombier d.length = atoi(field[3]); 6229a747e4fSDavid du Colombier d.atime = cracktime(field[4], field[5], field[6], field[7]); 6239a747e4fSDavid du Colombier break; 6249a747e4fSDavid du Colombier case 1: 6259a747e4fSDavid du Colombier s = s_copy(field[0]); 6269a747e4fSDavid du Colombier d.uid = "none"; 6279a747e4fSDavid du Colombier d.gid = d.uid; 6289a747e4fSDavid du Colombier d.mode = 0777; 6299a747e4fSDavid du Colombier d.atime = 0; 6309a747e4fSDavid du Colombier break; 6319a747e4fSDavid du Colombier default: 6329a747e4fSDavid du Colombier return nil; 6339a747e4fSDavid du Colombier } 6349a747e4fSDavid du Colombier break; 6359a747e4fSDavid du Colombier case Unix: 6369a747e4fSDavid du Colombier case Plan9: 6379a747e4fSDavid du Colombier default: 6389a747e4fSDavid du Colombier switch(n){ 6399a747e4fSDavid du Colombier case 8: /* ls -l */ 6409a747e4fSDavid du Colombier s = s_copy(field[7]); 6419a747e4fSDavid du Colombier d.uid = field[2]; 6429a747e4fSDavid du Colombier d.gid = d.uid; 6439a747e4fSDavid du Colombier d.mode = crackmode(field[0]); 6449a747e4fSDavid du Colombier d.length = atoi(field[3]); 6459a747e4fSDavid du Colombier if(strchr(field[6], ':')) 6469a747e4fSDavid du Colombier d.atime = cracktime(field[4], field[5], 0, field[6]); 6479a747e4fSDavid du Colombier else 6489a747e4fSDavid du Colombier d.atime = cracktime(field[4], field[5], field[6], 0); 6499a747e4fSDavid du Colombier break; 6509a747e4fSDavid du Colombier case 9: /* ls -lg */ 6519a747e4fSDavid du Colombier s = s_copy(field[8]); 6529a747e4fSDavid du Colombier d.uid = field[2]; 6539a747e4fSDavid du Colombier d.gid = field[3]; 6549a747e4fSDavid du Colombier d.mode = crackmode(field[0]); 6559a747e4fSDavid du Colombier d.length = atoi(field[4]); 6569a747e4fSDavid du Colombier if(strchr(field[7], ':')) 6579a747e4fSDavid du Colombier d.atime = cracktime(field[5], field[6], 0, field[7]); 6589a747e4fSDavid du Colombier else 6599a747e4fSDavid du Colombier d.atime = cracktime(field[5], field[6], field[7], 0); 6609a747e4fSDavid du Colombier break; 6619a747e4fSDavid du Colombier case 10: /* plan 9 */ 6629a747e4fSDavid du Colombier s = s_copy(field[9]); 6639a747e4fSDavid du Colombier d.uid = field[3]; 6649a747e4fSDavid du Colombier d.gid = field[4]; 6659a747e4fSDavid du Colombier d.mode = crackmode(field[0]); 6669a747e4fSDavid du Colombier d.length = atoi(field[5]); 6679a747e4fSDavid du Colombier if(strchr(field[8], ':')) 6689a747e4fSDavid du Colombier d.atime = cracktime(field[6], field[7], 0, field[8]); 6699a747e4fSDavid du Colombier else 6709a747e4fSDavid du Colombier d.atime = cracktime(field[6], field[7], field[8], 0); 6719a747e4fSDavid du Colombier break; 6729a747e4fSDavid du Colombier case 4: /* a Windows_NT version */ 6739a747e4fSDavid du Colombier s = s_copy(field[3]); 6749a747e4fSDavid du Colombier d.uid = "NT"; 6759a747e4fSDavid du Colombier d.gid = d.uid; 6769a747e4fSDavid du Colombier if(strcmp("<DIR>", field[2]) == 0){ 6779a747e4fSDavid du Colombier d.length = 0; 6789a747e4fSDavid du Colombier d.mode = DMDIR|0777; 6799a747e4fSDavid du Colombier } else { 6809a747e4fSDavid du Colombier d.mode = 0666; 6819a747e4fSDavid du Colombier d.length = atoi(field[2]); 6829a747e4fSDavid du Colombier } 6839a747e4fSDavid du Colombier dn = getfields(field[0], dfield, 4, 1, "/-"); 6849a747e4fSDavid du Colombier if(dn == 3) 6859a747e4fSDavid du Colombier d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]); 6869a747e4fSDavid du Colombier break; 6879a747e4fSDavid du Colombier case 1: 6889a747e4fSDavid du Colombier s = s_copy(field[0]); 6899a747e4fSDavid du Colombier d.uid = "none"; 6909a747e4fSDavid du Colombier d.gid = d.uid; 6919a747e4fSDavid du Colombier d.mode = 0777; 6929a747e4fSDavid du Colombier d.atime = 0; 6939a747e4fSDavid du Colombier break; 6949a747e4fSDavid du Colombier default: 6959a747e4fSDavid du Colombier return nil; 6969a747e4fSDavid du Colombier } 6979a747e4fSDavid du Colombier } 6989a747e4fSDavid du Colombier d.muid = d.uid; 6999a747e4fSDavid du Colombier d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE; 7009a747e4fSDavid du Colombier d.mtime = d.atime; 7019a747e4fSDavid du Colombier if(ext && (d.qid.type & QTDIR) == 0) 7029a747e4fSDavid du Colombier s_append(s, ext); 7039a747e4fSDavid du Colombier d.name = s_to_c(s); 7049a747e4fSDavid du Colombier 7059a747e4fSDavid du Colombier /* allocate a freeable dir structure */ 7069a747e4fSDavid du Colombier dp = reallocdir(&d, 0); 7079a747e4fSDavid du Colombier *remname = s; 7089a747e4fSDavid du Colombier 7099a747e4fSDavid du Colombier return dp; 7109a747e4fSDavid du Colombier } 7119a747e4fSDavid du Colombier 7129a747e4fSDavid du Colombier /* 7139a747e4fSDavid du Colombier * probe files in a directory to see if they are directories 7149a747e4fSDavid du Colombier */ 7159a747e4fSDavid du Colombier /* 7169a747e4fSDavid du Colombier * read a remote directory 7179a747e4fSDavid du Colombier */ 7189a747e4fSDavid du Colombier int 7199a747e4fSDavid du Colombier readdir(Node *node) 7209a747e4fSDavid du Colombier { 7219a747e4fSDavid du Colombier Biobuf *bp; 7229a747e4fSDavid du Colombier char *line; 7239a747e4fSDavid du Colombier Node *np; 7249a747e4fSDavid du Colombier Dir *d; 7259a747e4fSDavid du Colombier long n; 7269a747e4fSDavid du Colombier int tries, x, files; 7279a747e4fSDavid du Colombier static int uselist; 7289a747e4fSDavid du Colombier int usenlist; 7299a747e4fSDavid du Colombier String *remname; 7309a747e4fSDavid du Colombier 7319a747e4fSDavid du Colombier if(changedir(node) < 0) 7329a747e4fSDavid du Colombier return -1; 7339a747e4fSDavid du Colombier 7349a747e4fSDavid du Colombier usenlist = 0; 7359a747e4fSDavid du Colombier for(tries = 0; tries < 3; tries++){ 7369a747e4fSDavid du Colombier if(usenlist || usenlst) 7379a747e4fSDavid du Colombier x = data(OREAD, &bp, "NLST", nil); 7389a747e4fSDavid du Colombier else if(os == Unix && !uselist) 7399a747e4fSDavid du Colombier x = data(OREAD, &bp, "LIST -l", nil); 7409a747e4fSDavid du Colombier else 7419a747e4fSDavid du Colombier x = data(OREAD, &bp, "LIST", nil); 7429a747e4fSDavid du Colombier switch(x){ 7439a747e4fSDavid du Colombier case Extra: 7449a747e4fSDavid du Colombier break; 7459a747e4fSDavid du Colombier /* case TempFail: 7469a747e4fSDavid du Colombier continue; 7479a747e4fSDavid du Colombier */ 7489a747e4fSDavid du Colombier default: 7499a747e4fSDavid du Colombier if(os == Unix && uselist == 0){ 7509a747e4fSDavid du Colombier uselist = 1; 7519a747e4fSDavid du Colombier continue; 7529a747e4fSDavid du Colombier } 7539a747e4fSDavid du Colombier return seterr(nosuchfile); 7549a747e4fSDavid du Colombier } 7559a747e4fSDavid du Colombier files = 0; 7569a747e4fSDavid du Colombier while(line = Brdline(bp, '\n')){ 7579a747e4fSDavid du Colombier n = Blinelen(bp); 7589a747e4fSDavid du Colombier if(debug) 7599a747e4fSDavid du Colombier write(2, line, n); 7609a747e4fSDavid du Colombier if(n > 1 && line[n-2] == '\r') 7619a747e4fSDavid du Colombier n--; 7629a747e4fSDavid du Colombier line[n - 1] = 0; 7639a747e4fSDavid du Colombier 7649a747e4fSDavid du Colombier d = crackdir(line, &remname); 7659a747e4fSDavid du Colombier if(d == nil) 7669a747e4fSDavid du Colombier continue; 7679a747e4fSDavid du Colombier files++; 7689a747e4fSDavid du Colombier np = extendpath(node, remname); 7699a747e4fSDavid du Colombier d->qid.path = np->d->qid.path; 7709a747e4fSDavid du Colombier d->qid.vers = np->d->qid.vers; 7719a747e4fSDavid du Colombier d->type = np->d->type; 7729a747e4fSDavid du Colombier d->dev = 1; /* mark node as valid */ 7739a747e4fSDavid du Colombier if(os == MVS && node == remroot){ 7749a747e4fSDavid du Colombier d->qid.type = QTDIR; 7759a747e4fSDavid du Colombier d->mode |= DMDIR; 7769a747e4fSDavid du Colombier } 7779a747e4fSDavid du Colombier free(np->d); 7789a747e4fSDavid du Colombier np->d = d; 7799a747e4fSDavid du Colombier } 7809a747e4fSDavid du Colombier close(Bfildes(bp)); 7819a747e4fSDavid du Colombier 7829a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 7839a747e4fSDavid du Colombier case Success: 7849a747e4fSDavid du Colombier if(files == 0 && !usenlst && !usenlist){ 7859a747e4fSDavid du Colombier usenlist = 1; 7869a747e4fSDavid du Colombier continue; 7879a747e4fSDavid du Colombier } 7889a747e4fSDavid du Colombier if(files && usenlist) 7899a747e4fSDavid du Colombier usenlst = 1; 7909a747e4fSDavid du Colombier if(usenlst) 7919a747e4fSDavid du Colombier node->chdirunknown = 1; 7929a747e4fSDavid du Colombier return 0; 7939a747e4fSDavid du Colombier case TempFail: 7949a747e4fSDavid du Colombier break; 7959a747e4fSDavid du Colombier default: 7969a747e4fSDavid du Colombier return seterr(nosuchfile); 7979a747e4fSDavid du Colombier } 7989a747e4fSDavid du Colombier } 7999a747e4fSDavid du Colombier return seterr(nosuchfile); 8009a747e4fSDavid du Colombier } 8019a747e4fSDavid du Colombier 8029a747e4fSDavid du Colombier /* 8039a747e4fSDavid du Colombier * create a remote directory 8049a747e4fSDavid du Colombier */ 8059a747e4fSDavid du Colombier int 8069a747e4fSDavid du Colombier createdir(Node *node) 8079a747e4fSDavid du Colombier { 8089a747e4fSDavid du Colombier if(changedir(node->parent) < 0) 8099a747e4fSDavid du Colombier return -1; 8109a747e4fSDavid du Colombier 8119a747e4fSDavid du Colombier sendrequest("MKD", node->d->name); 8129a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 8139a747e4fSDavid du Colombier return -1; 8149a747e4fSDavid du Colombier return 0; 8159a747e4fSDavid du Colombier } 8169a747e4fSDavid du Colombier 8179a747e4fSDavid du Colombier /* 8189a747e4fSDavid du Colombier * change to a remote directory. 8199a747e4fSDavid du Colombier */ 8209a747e4fSDavid du Colombier int 8219a747e4fSDavid du Colombier changedir(Node *node) 8229a747e4fSDavid du Colombier { 8239a747e4fSDavid du Colombier Node *to; 8249a747e4fSDavid du Colombier String *cdpath; 8259a747e4fSDavid du Colombier 8269a747e4fSDavid du Colombier to = node; 8279a747e4fSDavid du Colombier if(to == remdir) 8289a747e4fSDavid du Colombier return 0; 8299a747e4fSDavid du Colombier 8309a747e4fSDavid du Colombier /* build an absolute path */ 8319a747e4fSDavid du Colombier switch(os){ 8329a747e4fSDavid du Colombier case Tops: 8339a747e4fSDavid du Colombier case VM: 8349a747e4fSDavid du Colombier switch(node->depth){ 8359a747e4fSDavid du Colombier case 0: 8369a747e4fSDavid du Colombier remdir = node; 8379a747e4fSDavid du Colombier return 0; 8389a747e4fSDavid du Colombier case 1: 8399a747e4fSDavid du Colombier cdpath = s_clone(node->remname); 8409a747e4fSDavid du Colombier break; 8419a747e4fSDavid du Colombier default: 8429a747e4fSDavid du Colombier return seterr(nosuchfile); 8439a747e4fSDavid du Colombier } 8449a747e4fSDavid du Colombier break; 8459a747e4fSDavid du Colombier case VMS: 8469a747e4fSDavid du Colombier switch(node->depth){ 8479a747e4fSDavid du Colombier case 0: 8489a747e4fSDavid du Colombier remdir = node; 8499a747e4fSDavid du Colombier return 0; 8509a747e4fSDavid du Colombier default: 8519a747e4fSDavid du Colombier cdpath = s_new(); 8529a747e4fSDavid du Colombier vmspath(node, cdpath); 8539a747e4fSDavid du Colombier } 8549a747e4fSDavid du Colombier break; 8559a747e4fSDavid du Colombier case MVS: 8569a747e4fSDavid du Colombier if(node->depth == 0) 8579a747e4fSDavid du Colombier cdpath = s_clone(remrootpath); 8589a747e4fSDavid du Colombier else{ 8599a747e4fSDavid du Colombier cdpath = s_new(); 8609a747e4fSDavid du Colombier mvspath(node, cdpath); 8619a747e4fSDavid du Colombier } 8629a747e4fSDavid du Colombier break; 8639a747e4fSDavid du Colombier default: 8649a747e4fSDavid du Colombier if(node->depth == 0) 8659a747e4fSDavid du Colombier cdpath = s_clone(remrootpath); 8669a747e4fSDavid du Colombier else{ 8679a747e4fSDavid du Colombier cdpath = s_new(); 8689a747e4fSDavid du Colombier unixpath(node, cdpath); 8699a747e4fSDavid du Colombier } 8709a747e4fSDavid du Colombier break; 8719a747e4fSDavid du Colombier } 8729a747e4fSDavid du Colombier 8739a747e4fSDavid du Colombier uncachedir(remdir, 0); 8749a747e4fSDavid du Colombier 8759a747e4fSDavid du Colombier /* 8769a747e4fSDavid du Colombier * connect, if we need a password (Incomplete) 8779a747e4fSDavid du Colombier * act like it worked (best we can do). 8789a747e4fSDavid du Colombier */ 8799a747e4fSDavid du Colombier sendrequest("CWD", s_to_c(cdpath)); 8809a747e4fSDavid du Colombier s_free(cdpath); 8819a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 8829a747e4fSDavid du Colombier case Success: 8839a747e4fSDavid du Colombier case Incomplete: 8849a747e4fSDavid du Colombier remdir = node; 8859a747e4fSDavid du Colombier return 0; 8869a747e4fSDavid du Colombier default: 8879a747e4fSDavid du Colombier return seterr(nosuchfile); 8889a747e4fSDavid du Colombier } 8899a747e4fSDavid du Colombier } 8909a747e4fSDavid du Colombier 8919a747e4fSDavid du Colombier /* 8929a747e4fSDavid du Colombier * read a remote file 8939a747e4fSDavid du Colombier */ 8949a747e4fSDavid du Colombier int 8959a747e4fSDavid du Colombier readfile1(Node *node) 8969a747e4fSDavid du Colombier { 8979a747e4fSDavid du Colombier Biobuf *bp; 8989a747e4fSDavid du Colombier char buf[4*1024]; 8999a747e4fSDavid du Colombier long off; 9009a747e4fSDavid du Colombier int n; 9019a747e4fSDavid du Colombier int tries; 9029a747e4fSDavid du Colombier 9039a747e4fSDavid du Colombier if(changedir(node->parent) < 0) 9049a747e4fSDavid du Colombier return -1; 9059a747e4fSDavid du Colombier 9069a747e4fSDavid du Colombier for(tries = 0; tries < 4; tries++){ 9079a747e4fSDavid du Colombier switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){ 9089a747e4fSDavid du Colombier case Extra: 9099a747e4fSDavid du Colombier break; 9109a747e4fSDavid du Colombier case TempFail: 9119a747e4fSDavid du Colombier continue; 9129a747e4fSDavid du Colombier default: 9139a747e4fSDavid du Colombier return seterr(nosuchfile); 9149a747e4fSDavid du Colombier } 9159a747e4fSDavid du Colombier off = 0; 9169a747e4fSDavid du Colombier while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){ 9179a747e4fSDavid du Colombier if(filewrite(node, buf, off, n) != n){ 9189a747e4fSDavid du Colombier off = -1; 9199a747e4fSDavid du Colombier break; 9209a747e4fSDavid du Colombier } 9219a747e4fSDavid du Colombier off += n; 9229a747e4fSDavid du Colombier } 9239a747e4fSDavid du Colombier if(off < 0) 9249a747e4fSDavid du Colombier return -1; 9259a747e4fSDavid du Colombier 9269a747e4fSDavid du Colombier /* make sure a file gets created even for a zero length file */ 9279a747e4fSDavid du Colombier if(off == 0) 9289a747e4fSDavid du Colombier filewrite(node, buf, 0, 0); 9299a747e4fSDavid du Colombier 9309a747e4fSDavid du Colombier close(Bfildes(bp)); 9319a747e4fSDavid du Colombier switch(getreply(&ctlin, msg, sizeof(msg), 0)){ 9329a747e4fSDavid du Colombier case Success: 9339a747e4fSDavid du Colombier return off; 9349a747e4fSDavid du Colombier case TempFail: 9359a747e4fSDavid du Colombier continue; 9369a747e4fSDavid du Colombier default: 9379a747e4fSDavid du Colombier return seterr(nosuchfile); 9389a747e4fSDavid du Colombier } 9399a747e4fSDavid du Colombier } 9409a747e4fSDavid du Colombier return seterr(nosuchfile); 9419a747e4fSDavid du Colombier } 9429a747e4fSDavid du Colombier 9439a747e4fSDavid du Colombier int 9449a747e4fSDavid du Colombier readfile(Node *node) 9459a747e4fSDavid du Colombier { 9469a747e4fSDavid du Colombier int rv, inimage; 9479a747e4fSDavid du Colombier 9489a747e4fSDavid du Colombier switch(os){ 9499a747e4fSDavid du Colombier case MVS: 9509a747e4fSDavid du Colombier case Plan9: 9519a747e4fSDavid du Colombier case Tops: 9529a747e4fSDavid du Colombier case TSO: 9539a747e4fSDavid du Colombier inimage = 0; 9549a747e4fSDavid du Colombier break; 9559a747e4fSDavid du Colombier default: 9569a747e4fSDavid du Colombier inimage = 1; 9579a747e4fSDavid du Colombier image(); 9589a747e4fSDavid du Colombier break; 9599a747e4fSDavid du Colombier } 9609a747e4fSDavid du Colombier 9619a747e4fSDavid du Colombier rv = readfile1(node); 9629a747e4fSDavid du Colombier 9639a747e4fSDavid du Colombier if(inimage) 9649a747e4fSDavid du Colombier ascii(); 9659a747e4fSDavid du Colombier return rv; 9669a747e4fSDavid du Colombier } 9679a747e4fSDavid du Colombier 9689a747e4fSDavid du Colombier /* 9699a747e4fSDavid du Colombier * write back a file 9709a747e4fSDavid du Colombier */ 9719a747e4fSDavid du Colombier int 9729a747e4fSDavid du Colombier createfile1(Node *node) 9739a747e4fSDavid du Colombier { 9749a747e4fSDavid du Colombier Biobuf *bp; 9759a747e4fSDavid du Colombier char buf[4*1024]; 9769a747e4fSDavid du Colombier long off; 9779a747e4fSDavid du Colombier int n; 9789a747e4fSDavid du Colombier 9799a747e4fSDavid du Colombier if(changedir(node->parent) < 0) 9809a747e4fSDavid du Colombier return -1; 9819a747e4fSDavid du Colombier 9829a747e4fSDavid du Colombier if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra) 9839a747e4fSDavid du Colombier return -1; 9849a747e4fSDavid du Colombier for(off = 0; ; off += n){ 9859a747e4fSDavid du Colombier n = fileread(node, buf, off, sizeof(buf)); 9869a747e4fSDavid du Colombier if(n <= 0) 9879a747e4fSDavid du Colombier break; 9889a747e4fSDavid du Colombier write(Bfildes(bp), buf, n); 9899a747e4fSDavid du Colombier } 9909a747e4fSDavid du Colombier close(Bfildes(bp)); 991*05a04ac5SDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 992*05a04ac5SDavid du Colombier return -1; 9939a747e4fSDavid du Colombier return off; 9949a747e4fSDavid du Colombier } 9959a747e4fSDavid du Colombier 9969a747e4fSDavid du Colombier int 9979a747e4fSDavid du Colombier createfile(Node *node) 9989a747e4fSDavid du Colombier { 9999a747e4fSDavid du Colombier int rv; 10009a747e4fSDavid du Colombier 10019a747e4fSDavid du Colombier switch(os){ 10029a747e4fSDavid du Colombier case Plan9: 10039a747e4fSDavid du Colombier case Tops: 10049a747e4fSDavid du Colombier break; 10059a747e4fSDavid du Colombier default: 10069a747e4fSDavid du Colombier image(); 10079a747e4fSDavid du Colombier break; 10089a747e4fSDavid du Colombier } 10099a747e4fSDavid du Colombier rv = createfile1(node); 10109a747e4fSDavid du Colombier switch(os){ 10119a747e4fSDavid du Colombier case Plan9: 10129a747e4fSDavid du Colombier case Tops: 10139a747e4fSDavid du Colombier break; 10149a747e4fSDavid du Colombier default: 10159a747e4fSDavid du Colombier ascii(); 10169a747e4fSDavid du Colombier break; 10179a747e4fSDavid du Colombier } 10189a747e4fSDavid du Colombier return rv; 10199a747e4fSDavid du Colombier } 10209a747e4fSDavid du Colombier 10219a747e4fSDavid du Colombier /* 10229a747e4fSDavid du Colombier * remove a remote file 10239a747e4fSDavid du Colombier */ 10249a747e4fSDavid du Colombier int 10259a747e4fSDavid du Colombier removefile(Node *node) 10269a747e4fSDavid du Colombier { 10279a747e4fSDavid du Colombier if(changedir(node->parent) < 0) 10289a747e4fSDavid du Colombier return -1; 10299a747e4fSDavid du Colombier 10309a747e4fSDavid du Colombier sendrequest("DELE", s_to_c(node->remname)); 10319a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 10329a747e4fSDavid du Colombier return -1; 10339a747e4fSDavid du Colombier return 0; 10349a747e4fSDavid du Colombier } 10359a747e4fSDavid du Colombier 10369a747e4fSDavid du Colombier /* 10379a747e4fSDavid du Colombier * remove a remote directory 10389a747e4fSDavid du Colombier */ 10399a747e4fSDavid du Colombier int 10409a747e4fSDavid du Colombier removedir(Node *node) 10419a747e4fSDavid du Colombier { 10429a747e4fSDavid du Colombier if(changedir(node->parent) < 0) 10439a747e4fSDavid du Colombier return -1; 10449a747e4fSDavid du Colombier 10459a747e4fSDavid du Colombier sendrequest("RMD", s_to_c(node->remname)); 10469a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 10479a747e4fSDavid du Colombier return -1; 10489a747e4fSDavid du Colombier return 0; 10499a747e4fSDavid du Colombier } 10509a747e4fSDavid du Colombier 10519a747e4fSDavid du Colombier /* 10529a747e4fSDavid du Colombier * tell remote that we're exiting and then do it 10539a747e4fSDavid du Colombier */ 10549a747e4fSDavid du Colombier void 10559a747e4fSDavid du Colombier quit(void) 10569a747e4fSDavid du Colombier { 10579a747e4fSDavid du Colombier sendrequest("QUIT", nil); 10589a747e4fSDavid du Colombier getreply(&ctlin, msg, sizeof(msg), 0); 10599a747e4fSDavid du Colombier exits(0); 10609a747e4fSDavid du Colombier } 10619a747e4fSDavid du Colombier 10629a747e4fSDavid du Colombier /* 10639a747e4fSDavid du Colombier * send a request 10649a747e4fSDavid du Colombier */ 10659a747e4fSDavid du Colombier static void 10669a747e4fSDavid du Colombier sendrequest(char *a, char *b) 10679a747e4fSDavid du Colombier { 10689a747e4fSDavid du Colombier char buf[2*1024]; 10699a747e4fSDavid du Colombier int n; 10709a747e4fSDavid du Colombier 10719a747e4fSDavid du Colombier n = strlen(a)+2+1; 10729a747e4fSDavid du Colombier if(b != nil) 10739a747e4fSDavid du Colombier n += strlen(b)+1; 10749a747e4fSDavid du Colombier if(n >= sizeof(buf)) 10759a747e4fSDavid du Colombier fatal("proto request too long"); 10769a747e4fSDavid du Colombier strcpy(buf, a); 10779a747e4fSDavid du Colombier if(b != nil){ 10789a747e4fSDavid du Colombier strcat(buf, " "); 10799a747e4fSDavid du Colombier strcat(buf, b); 10809a747e4fSDavid du Colombier } 10819a747e4fSDavid du Colombier strcat(buf, "\r\n"); 10829a747e4fSDavid du Colombier n = strlen(buf); 10839a747e4fSDavid du Colombier if(write(ctlfd, buf, n) != n) 10849a747e4fSDavid du Colombier fatal("remote side hung up"); 10859a747e4fSDavid du Colombier if(debug) 10869a747e4fSDavid du Colombier write(2, buf, n); 10879a747e4fSDavid du Colombier lastsend = time(0); 10889a747e4fSDavid du Colombier } 10899a747e4fSDavid du Colombier 10909a747e4fSDavid du Colombier /* 10919a747e4fSDavid du Colombier * replies codes are in the range [100, 999] and may contain multiple lines of 10929a747e4fSDavid du Colombier * continuation. 10939a747e4fSDavid du Colombier */ 10949a747e4fSDavid du Colombier static int 10959a747e4fSDavid du Colombier getreply(Biobuf *bp, char *msg, int len, int printreply) 10969a747e4fSDavid du Colombier { 10979a747e4fSDavid du Colombier char *line; 1098d9306527SDavid du Colombier char *p; 10999a747e4fSDavid du Colombier int rv; 11009a747e4fSDavid du Colombier int i, n; 11019a747e4fSDavid du Colombier 11029a747e4fSDavid du Colombier while(line = Brdline(bp, '\n')){ 11039a747e4fSDavid du Colombier /* add line to message buffer, strip off \r */ 11049a747e4fSDavid du Colombier n = Blinelen(bp); 11059a747e4fSDavid du Colombier if(n > 1 && line[n-2] == '\r'){ 11069a747e4fSDavid du Colombier n--; 11079a747e4fSDavid du Colombier line[n-1] = '\n'; 11089a747e4fSDavid du Colombier } 11099a747e4fSDavid du Colombier if(printreply && !quiet) 11109a747e4fSDavid du Colombier write(1, line, n); 11119a747e4fSDavid du Colombier else if(debug) 11129a747e4fSDavid du Colombier write(2, line, n); 11139a747e4fSDavid du Colombier if(n > len - 1) 11149a747e4fSDavid du Colombier i = len - 1; 11159a747e4fSDavid du Colombier else 11169a747e4fSDavid du Colombier i = n; 11179a747e4fSDavid du Colombier if(i > 0){ 11189a747e4fSDavid du Colombier memmove(msg, line, i); 11199a747e4fSDavid du Colombier msg += i; 11209a747e4fSDavid du Colombier len -= i; 11219a747e4fSDavid du Colombier *msg = 0; 11229a747e4fSDavid du Colombier } 11239a747e4fSDavid du Colombier 11249a747e4fSDavid du Colombier /* stop if not a continuation */ 1125d9306527SDavid du Colombier rv = strtol(line, &p, 10); 1126d9306527SDavid du Colombier if(rv >= 100 && rv < 600 && p==line+3 && *p != '-') 11279a747e4fSDavid du Colombier return rv/100; 11289a747e4fSDavid du Colombier 11299a747e4fSDavid du Colombier /* tell user about continuations */ 11309a747e4fSDavid du Colombier if(!debug && !quiet && !printreply) 11319a747e4fSDavid du Colombier write(2, line, n); 11329a747e4fSDavid du Colombier } 11339a747e4fSDavid du Colombier 11349a747e4fSDavid du Colombier fatal("remote side closed connection"); 11359a747e4fSDavid du Colombier return 0; 11369a747e4fSDavid du Colombier } 11379a747e4fSDavid du Colombier 11389a747e4fSDavid du Colombier /* 11399a747e4fSDavid du Colombier * Announce on a local port and tell its address to the the remote side 11409a747e4fSDavid du Colombier */ 11419a747e4fSDavid du Colombier static int 11429a747e4fSDavid du Colombier port(void) 11439a747e4fSDavid du Colombier { 11449a747e4fSDavid du Colombier char buf[256]; 11459a747e4fSDavid du Colombier int n, fd; 11469a747e4fSDavid du Colombier char *ptr; 11479a747e4fSDavid du Colombier uchar ipaddr[IPaddrlen]; 11489a747e4fSDavid du Colombier int port; 11499a747e4fSDavid du Colombier 11509a747e4fSDavid du Colombier /* get a channel to listen on, let kernel pick the port number */ 11519a747e4fSDavid du Colombier sprint(buf, "%s!*!0", net); 11529a747e4fSDavid du Colombier listenfd = announce(buf, netdir); 11539a747e4fSDavid du Colombier if(listenfd < 0) 11549a747e4fSDavid du Colombier return seterr("can't announce"); 11559a747e4fSDavid du Colombier 11569a747e4fSDavid du Colombier /* get the local address and port number */ 11579a747e4fSDavid du Colombier sprint(buf, "%s/local", netdir); 11589a747e4fSDavid du Colombier fd = open(buf, OREAD); 11599a747e4fSDavid du Colombier if(fd < 0) 11609a747e4fSDavid du Colombier return seterr("opening %s: %r", buf); 11619a747e4fSDavid du Colombier n = read(fd, buf, sizeof(buf)-1); 11629a747e4fSDavid du Colombier close(fd); 11639a747e4fSDavid du Colombier if(n <= 0) 11649a747e4fSDavid du Colombier return seterr("opening %s/local: %r", netdir); 11659a747e4fSDavid du Colombier buf[n] = 0; 11669a747e4fSDavid du Colombier ptr = strchr(buf, ' '); 11679a747e4fSDavid du Colombier if(ptr) 11689a747e4fSDavid du Colombier *ptr = 0; 11699a747e4fSDavid du Colombier ptr = strchr(buf, '!')+1; 11709a747e4fSDavid du Colombier parseip(ipaddr, buf); 11719a747e4fSDavid du Colombier port = atoi(ptr); 11729a747e4fSDavid du Colombier 11739a747e4fSDavid du Colombier /* tell remote side */ 11749a747e4fSDavid du Colombier sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1], 11759a747e4fSDavid du Colombier ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff); 11769a747e4fSDavid du Colombier sendrequest(buf, nil); 11779a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 0) != Success) 11789a747e4fSDavid du Colombier return seterr(msg); 11799a747e4fSDavid du Colombier return 0; 11809a747e4fSDavid du Colombier } 11819a747e4fSDavid du Colombier 11829a747e4fSDavid du Colombier /* 11839a747e4fSDavid du Colombier * have server call back for a data connection 11849a747e4fSDavid du Colombier */ 11859a747e4fSDavid du Colombier static int 11869a747e4fSDavid du Colombier active(int mode, Biobuf **bpp, char *cmda, char *cmdb) 11879a747e4fSDavid du Colombier { 11889a747e4fSDavid du Colombier int cfd, dfd, rv; 11899a747e4fSDavid du Colombier char newdir[Maxpath]; 11909a747e4fSDavid du Colombier char datafile[Maxpath + 6]; 11919a747e4fSDavid du Colombier 11929a747e4fSDavid du Colombier if(port() < 0) 11939a747e4fSDavid du Colombier return TempFail; 11949a747e4fSDavid du Colombier 11959a747e4fSDavid du Colombier sendrequest(cmda, cmdb); 11969a747e4fSDavid du Colombier 11979a747e4fSDavid du Colombier rv = getreply(&ctlin, msg, sizeof(msg), 0); 11989a747e4fSDavid du Colombier if(rv != Extra){ 11999a747e4fSDavid du Colombier close(listenfd); 12009a747e4fSDavid du Colombier return rv; 12019a747e4fSDavid du Colombier } 12029a747e4fSDavid du Colombier 12039a747e4fSDavid du Colombier /* wait for a new call */ 12049a747e4fSDavid du Colombier cfd = listen(netdir, newdir); 12059a747e4fSDavid du Colombier if(cfd < 0) 12069a747e4fSDavid du Colombier fatal("waiting for data connection"); 12079a747e4fSDavid du Colombier close(listenfd); 12089a747e4fSDavid du Colombier 12099a747e4fSDavid du Colombier /* open it's data connection and close the control connection */ 12109a747e4fSDavid du Colombier sprint(datafile, "%s/data", newdir); 12119a747e4fSDavid du Colombier dfd = open(datafile, ORDWR); 12129a747e4fSDavid du Colombier close(cfd); 12139a747e4fSDavid du Colombier if(dfd < 0) 12149a747e4fSDavid du Colombier fatal("opening data connection"); 12159a747e4fSDavid du Colombier Binit(&dbuf, dfd, mode); 12169a747e4fSDavid du Colombier *bpp = &dbuf; 12179a747e4fSDavid du Colombier return Extra; 12189a747e4fSDavid du Colombier } 12199a747e4fSDavid du Colombier 12209a747e4fSDavid du Colombier /* 12219a747e4fSDavid du Colombier * call out for a data connection 12229a747e4fSDavid du Colombier */ 12239a747e4fSDavid du Colombier static int 12249a747e4fSDavid du Colombier passive(int mode, Biobuf **bpp, char *cmda, char *cmdb) 12259a747e4fSDavid du Colombier { 12269a747e4fSDavid du Colombier char msg[1024]; 12279a747e4fSDavid du Colombier char ds[1024]; 12289a747e4fSDavid du Colombier char *f[6]; 12299a747e4fSDavid du Colombier char *p; 12309a747e4fSDavid du Colombier int x, fd; 12319a747e4fSDavid du Colombier 12329a747e4fSDavid du Colombier if(nopassive) 12339a747e4fSDavid du Colombier return Impossible; 12349a747e4fSDavid du Colombier 12359a747e4fSDavid du Colombier sendrequest("PASV", nil); 12369a747e4fSDavid du Colombier if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){ 12379a747e4fSDavid du Colombier nopassive = 1; 12389a747e4fSDavid du Colombier return Impossible; 12399a747e4fSDavid du Colombier } 12409a747e4fSDavid du Colombier 12419a747e4fSDavid du Colombier /* get address and port number from reply, this is AI */ 12429a747e4fSDavid du Colombier p = strchr(msg, '('); 12439a747e4fSDavid du Colombier if(p == 0){ 12449a747e4fSDavid du Colombier for(p = msg+3; *p; p++) 12459a747e4fSDavid du Colombier if(isdigit(*p)) 12469a747e4fSDavid du Colombier break; 12479a747e4fSDavid du Colombier } else 12489a747e4fSDavid du Colombier p++; 12499a747e4fSDavid du Colombier if(getfields(p, f, 6, 0, ",") < 6){ 12509a747e4fSDavid du Colombier if(debug) 12519a747e4fSDavid du Colombier fprint(2, "passive mode protocol botch: %s\n", msg); 12529a747e4fSDavid du Colombier werrstr("ftp protocol botch"); 12539a747e4fSDavid du Colombier nopassive = 1; 12549a747e4fSDavid du Colombier return Impossible; 12559a747e4fSDavid du Colombier } 12569a747e4fSDavid du Colombier snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net, 12579a747e4fSDavid du Colombier f[0], f[1], f[2], f[3], 12589a747e4fSDavid du Colombier ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff)); 12599a747e4fSDavid du Colombier 12609a747e4fSDavid du Colombier /* open data connection */ 12619a747e4fSDavid du Colombier fd = dial(ds, 0, 0, 0); 12629a747e4fSDavid du Colombier if(fd < 0){ 12639a747e4fSDavid du Colombier if(debug) 12649a747e4fSDavid du Colombier fprint(2, "passive mode connect to %s failed: %r\n", ds); 12659a747e4fSDavid du Colombier nopassive = 1; 12669a747e4fSDavid du Colombier return TempFail; 12679a747e4fSDavid du Colombier } 12689a747e4fSDavid du Colombier 12699a747e4fSDavid du Colombier /* tell remote to send a file */ 12709a747e4fSDavid du Colombier sendrequest(cmda, cmdb); 12719a747e4fSDavid du Colombier x = getreply(&ctlin, msg, sizeof(msg), 0); 12729a747e4fSDavid du Colombier if(x != Extra){ 12739a747e4fSDavid du Colombier close(fd); 12749a747e4fSDavid du Colombier if(debug) 12759a747e4fSDavid du Colombier fprint(2, "passive mode retrieve failed: %s\n", msg); 12769a747e4fSDavid du Colombier werrstr(msg); 12779a747e4fSDavid du Colombier return x; 12789a747e4fSDavid du Colombier } 12799a747e4fSDavid du Colombier 12809a747e4fSDavid du Colombier Binit(&dbuf, fd, mode); 12819a747e4fSDavid du Colombier *bpp = &dbuf; 12829a747e4fSDavid du Colombier return Extra; 12839a747e4fSDavid du Colombier } 12849a747e4fSDavid du Colombier 12859a747e4fSDavid du Colombier static int 12869a747e4fSDavid du Colombier data(int mode, Biobuf **bpp, char* cmda, char *cmdb) 12879a747e4fSDavid du Colombier { 12889a747e4fSDavid du Colombier int x; 12899a747e4fSDavid du Colombier 12909a747e4fSDavid du Colombier x = passive(mode, bpp, cmda, cmdb); 12919a747e4fSDavid du Colombier if(x != Impossible) 12929a747e4fSDavid du Colombier return x; 12939a747e4fSDavid du Colombier return active(mode, bpp, cmda, cmdb); 12949a747e4fSDavid du Colombier } 12959a747e4fSDavid du Colombier 12969a747e4fSDavid du Colombier /* 12979a747e4fSDavid du Colombier * used for keep alives 12989a747e4fSDavid du Colombier */ 12999a747e4fSDavid du Colombier void 13009a747e4fSDavid du Colombier nop(void) 13019a747e4fSDavid du Colombier { 13029a747e4fSDavid du Colombier if(lastsend - time(0) < 15) 13039a747e4fSDavid du Colombier return; 13049a747e4fSDavid du Colombier sendrequest("PWD", nil); 13059a747e4fSDavid du Colombier getreply(&ctlin, msg, sizeof(msg), 0); 13069a747e4fSDavid du Colombier } 13079a747e4fSDavid du Colombier 13089a747e4fSDavid du Colombier /* 13099a747e4fSDavid du Colombier * turn a vms spec into a path 13109a747e4fSDavid du Colombier */ 13119a747e4fSDavid du Colombier static Node* 13129a747e4fSDavid du Colombier vmsextendpath(Node *np, char *name) 13139a747e4fSDavid du Colombier { 13149a747e4fSDavid du Colombier np = extendpath(np, s_copy(name)); 13159a747e4fSDavid du Colombier if(!ISVALID(np)){ 13169a747e4fSDavid du Colombier np->d->qid.type = QTDIR; 13179a747e4fSDavid du Colombier np->d->atime = time(0); 13189a747e4fSDavid du Colombier np->d->mtime = np->d->atime; 13199a747e4fSDavid du Colombier strcpy(np->d->uid, "who"); 13209a747e4fSDavid du Colombier strcpy(np->d->gid, "cares"); 13219a747e4fSDavid du Colombier np->d->mode = DMDIR|0777; 13229a747e4fSDavid du Colombier np->d->length = 0; 13239a747e4fSDavid du Colombier if(changedir(np) >= 0) 13249a747e4fSDavid du Colombier VALID(np); 13259a747e4fSDavid du Colombier } 13269a747e4fSDavid du Colombier return np; 13279a747e4fSDavid du Colombier } 13289a747e4fSDavid du Colombier static Node* 13299a747e4fSDavid du Colombier vmsdir(char *name) 13309a747e4fSDavid du Colombier { 13319a747e4fSDavid du Colombier char *cp; 13329a747e4fSDavid du Colombier Node *np; 13339a747e4fSDavid du Colombier char *oname; 13349a747e4fSDavid du Colombier 13359a747e4fSDavid du Colombier np = remroot; 13369a747e4fSDavid du Colombier cp = strchr(name, '['); 13379a747e4fSDavid du Colombier if(cp) 13389a747e4fSDavid du Colombier strcpy(cp, cp+1); 13399a747e4fSDavid du Colombier cp = strchr(name, ']'); 13409a747e4fSDavid du Colombier if(cp) 13419a747e4fSDavid du Colombier *cp = 0; 13429a747e4fSDavid du Colombier oname = name = strdup(name); 13439a747e4fSDavid du Colombier if(name == 0) 13449a747e4fSDavid du Colombier return 0; 13459a747e4fSDavid du Colombier 13469a747e4fSDavid du Colombier while(cp = strchr(name, '.')){ 13479a747e4fSDavid du Colombier *cp = 0; 13489a747e4fSDavid du Colombier np = vmsextendpath(np, name); 13499a747e4fSDavid du Colombier name = cp+1; 13509a747e4fSDavid du Colombier } 13519a747e4fSDavid du Colombier np = vmsextendpath(np, name); 13529a747e4fSDavid du Colombier 13539a747e4fSDavid du Colombier /* 13549a747e4fSDavid du Colombier * walk back to first accessible directory 13559a747e4fSDavid du Colombier */ 13569a747e4fSDavid du Colombier for(; np->parent != np; np = np->parent) 13579a747e4fSDavid du Colombier if(ISVALID(np)){ 13589a747e4fSDavid du Colombier CACHED(np->parent); 13599a747e4fSDavid du Colombier break; 13609a747e4fSDavid du Colombier } 13619a747e4fSDavid du Colombier 13629a747e4fSDavid du Colombier free(oname); 13639a747e4fSDavid du Colombier return np; 13649a747e4fSDavid du Colombier } 13659a747e4fSDavid du Colombier 13669a747e4fSDavid du Colombier /* 13679a747e4fSDavid du Colombier * walk up the tree building a VMS style path 13689a747e4fSDavid du Colombier */ 13699a747e4fSDavid du Colombier static void 13709a747e4fSDavid du Colombier vmspath(Node *node, String *path) 13719a747e4fSDavid du Colombier { 13729a747e4fSDavid du Colombier char *p; 13739a747e4fSDavid du Colombier int n; 13749a747e4fSDavid du Colombier 13759a747e4fSDavid du Colombier if(node->depth == 1){ 13769a747e4fSDavid du Colombier p = strchr(s_to_c(node->remname), ':'); 13779a747e4fSDavid du Colombier if(p){ 13789a747e4fSDavid du Colombier n = p - s_to_c(node->remname) + 1; 13799a747e4fSDavid du Colombier s_nappend(path, s_to_c(node->remname), n); 13809a747e4fSDavid du Colombier s_append(path, "["); 13819a747e4fSDavid du Colombier s_append(path, p+1); 13829a747e4fSDavid du Colombier } else { 13839a747e4fSDavid du Colombier s_append(path, "["); 13849a747e4fSDavid du Colombier s_append(path, s_to_c(node->remname)); 13859a747e4fSDavid du Colombier } 13869a747e4fSDavid du Colombier s_append(path, "]"); 13879a747e4fSDavid du Colombier return; 13889a747e4fSDavid du Colombier } 13899a747e4fSDavid du Colombier vmspath(node->parent, path); 13909a747e4fSDavid du Colombier s_append(path, "."); 13919a747e4fSDavid du Colombier s_append(path, s_to_c(node->remname)); 13929a747e4fSDavid du Colombier } 13939a747e4fSDavid du Colombier 13949a747e4fSDavid du Colombier /* 13959a747e4fSDavid du Colombier * walk up the tree building a Unix style path 13969a747e4fSDavid du Colombier */ 13979a747e4fSDavid du Colombier static void 13989a747e4fSDavid du Colombier unixpath(Node *node, String *path) 13999a747e4fSDavid du Colombier { 14009a747e4fSDavid du Colombier if(node == node->parent){ 14019a747e4fSDavid du Colombier s_append(path, s_to_c(remrootpath)); 14029a747e4fSDavid du Colombier return; 14039a747e4fSDavid du Colombier } 14049a747e4fSDavid du Colombier unixpath(node->parent, path); 1405ecc2a59cSDavid du Colombier if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0) 14069a747e4fSDavid du Colombier s_append(path, "/"); 14079a747e4fSDavid du Colombier s_append(path, s_to_c(node->remname)); 14089a747e4fSDavid du Colombier } 14099a747e4fSDavid du Colombier 14109a747e4fSDavid du Colombier /* 14119a747e4fSDavid du Colombier * walk up the tree building a MVS style path 14129a747e4fSDavid du Colombier */ 14139a747e4fSDavid du Colombier static void 14149a747e4fSDavid du Colombier mvspath(Node *node, String *path) 14159a747e4fSDavid du Colombier { 14169a747e4fSDavid du Colombier if(node == node->parent){ 14179a747e4fSDavid du Colombier s_append(path, s_to_c(remrootpath)); 14189a747e4fSDavid du Colombier return; 14199a747e4fSDavid du Colombier } 14209a747e4fSDavid du Colombier mvspath(node->parent, path); 14219a747e4fSDavid du Colombier if(s_len(path) > 0) 14229a747e4fSDavid du Colombier s_append(path, "."); 14239a747e4fSDavid du Colombier s_append(path, s_to_c(node->remname)); 14249a747e4fSDavid du Colombier } 14259a747e4fSDavid du Colombier 14269a747e4fSDavid du Colombier static int 14279a747e4fSDavid du Colombier getpassword(char *buf, char *e) 14289a747e4fSDavid du Colombier { 14299a747e4fSDavid du Colombier char *p; 14309a747e4fSDavid du Colombier int c; 14319a747e4fSDavid du Colombier int consctl, rv = 0; 14329a747e4fSDavid du Colombier 14339a747e4fSDavid du Colombier consctl = open("/dev/consctl", OWRITE); 14349a747e4fSDavid du Colombier if(consctl >= 0) 14359a747e4fSDavid du Colombier write(consctl, "rawon", 5); 14369a747e4fSDavid du Colombier print("Password: "); 14379a747e4fSDavid du Colombier e--; 14389a747e4fSDavid du Colombier for(p = buf; p <= e; p++){ 14399a747e4fSDavid du Colombier c = Bgetc(&stdin); 14409a747e4fSDavid du Colombier if(c < 0){ 14419a747e4fSDavid du Colombier rv = -1; 14429a747e4fSDavid du Colombier goto out; 14439a747e4fSDavid du Colombier } 14449a747e4fSDavid du Colombier if(c == '\n' || c == '\r') 14459a747e4fSDavid du Colombier break; 14469a747e4fSDavid du Colombier *p = c; 14479a747e4fSDavid du Colombier } 14489a747e4fSDavid du Colombier *p = 0; 14499a747e4fSDavid du Colombier print("\n"); 14509a747e4fSDavid du Colombier 14519a747e4fSDavid du Colombier out: 14529a747e4fSDavid du Colombier if(consctl >= 0) 14539a747e4fSDavid du Colombier close(consctl); 14549a747e4fSDavid du Colombier return rv; 14559a747e4fSDavid du Colombier } 14569a747e4fSDavid du Colombier 14579a747e4fSDavid du Colombier /* 14589a747e4fSDavid du Colombier * convert from latin1 to utf 14599a747e4fSDavid du Colombier */ 14609a747e4fSDavid du Colombier static char* 14619a747e4fSDavid du Colombier fromlatin1(char *from) 14629a747e4fSDavid du Colombier { 14639a747e4fSDavid du Colombier char *p, *to; 14649a747e4fSDavid du Colombier Rune r; 14659a747e4fSDavid du Colombier 14669a747e4fSDavid du Colombier if(os == Plan9) 14679a747e4fSDavid du Colombier return nil; 14689a747e4fSDavid du Colombier 14699a747e4fSDavid du Colombier /* don't convert if we don't have to */ 14709a747e4fSDavid du Colombier for(p = from; *p; p++) 14719a747e4fSDavid du Colombier if(*p & 0x80) 14729a747e4fSDavid du Colombier break; 14739a747e4fSDavid du Colombier if(*p == 0) 14749a747e4fSDavid du Colombier return nil; 14759a747e4fSDavid du Colombier 14769a747e4fSDavid du Colombier to = malloc(3*strlen(from)+2); 14779a747e4fSDavid du Colombier if(to == nil) 14789a747e4fSDavid du Colombier return nil; 14799a747e4fSDavid du Colombier for(p = to; *from; from++){ 14809a747e4fSDavid du Colombier r = (*from) & 0xff; 14819a747e4fSDavid du Colombier p += runetochar(p, &r); 14829a747e4fSDavid du Colombier } 14839a747e4fSDavid du Colombier *p = 0; 14849a747e4fSDavid du Colombier return to; 14859a747e4fSDavid du Colombier } 14869a747e4fSDavid du Colombier 14879a747e4fSDavid du Colombier Dir* 14889a747e4fSDavid du Colombier reallocdir(Dir *d, int dofree) 14899a747e4fSDavid du Colombier { 14909a747e4fSDavid du Colombier Dir *dp; 14919a747e4fSDavid du Colombier char *p; 14929a747e4fSDavid du Colombier int nn, ng, nu, nm; 14939a747e4fSDavid du Colombier char *utf; 14949a747e4fSDavid du Colombier 14959a747e4fSDavid du Colombier if(d->name == nil) 14969a747e4fSDavid du Colombier d->name = "?name?"; 14979a747e4fSDavid du Colombier if(d->uid == nil) 14989a747e4fSDavid du Colombier d->uid = "?uid?"; 14999a747e4fSDavid du Colombier if(d->gid == nil) 15009a747e4fSDavid du Colombier d->gid = d->uid; 15019a747e4fSDavid du Colombier if(d->muid == nil) 15029a747e4fSDavid du Colombier d->muid = d->uid; 15039a747e4fSDavid du Colombier 15049a747e4fSDavid du Colombier utf = fromlatin1(d->name); 15059a747e4fSDavid du Colombier if(utf != nil) 15069a747e4fSDavid du Colombier d->name = utf; 15079a747e4fSDavid du Colombier 15089a747e4fSDavid du Colombier nn = strlen(d->name)+1; 15099a747e4fSDavid du Colombier nu = strlen(d->uid)+1; 15109a747e4fSDavid du Colombier ng = strlen(d->gid)+1; 15119a747e4fSDavid du Colombier nm = strlen(d->muid)+1; 15129a747e4fSDavid du Colombier dp = malloc(sizeof(Dir)+nn+nu+ng+nm); 15139a747e4fSDavid du Colombier if(dp == nil){ 15149a747e4fSDavid du Colombier if(dofree) 15159a747e4fSDavid du Colombier free(d); 15169a747e4fSDavid du Colombier if(utf != nil) 15179a747e4fSDavid du Colombier free(utf); 15189a747e4fSDavid du Colombier return nil; 15199a747e4fSDavid du Colombier } 15209a747e4fSDavid du Colombier *dp = *d; 15219a747e4fSDavid du Colombier p = (char*)&dp[1]; 15229a747e4fSDavid du Colombier strcpy(p, d->name); 15239a747e4fSDavid du Colombier dp->name = p; 15249a747e4fSDavid du Colombier p += nn; 15259a747e4fSDavid du Colombier strcpy(p, d->uid); 15269a747e4fSDavid du Colombier dp->uid = p; 15279a747e4fSDavid du Colombier p += nu; 15289a747e4fSDavid du Colombier strcpy(p, d->gid); 15299a747e4fSDavid du Colombier dp->gid = p; 15309a747e4fSDavid du Colombier p += ng; 15319a747e4fSDavid du Colombier strcpy(p, d->muid); 15329a747e4fSDavid du Colombier dp->muid = p; 15339a747e4fSDavid du Colombier if(dofree) 15349a747e4fSDavid du Colombier free(d); 15359a747e4fSDavid du Colombier if(utf != nil) 15369a747e4fSDavid du Colombier free(utf); 15379a747e4fSDavid du Colombier return dp; 15389a747e4fSDavid du Colombier } 15399a747e4fSDavid du Colombier 15409a747e4fSDavid du Colombier Dir* 15419a747e4fSDavid du Colombier dir_change_name(Dir *d, char *name) 15429a747e4fSDavid du Colombier { 15439a747e4fSDavid du Colombier if(d->name && strlen(d->name) >= strlen(name)){ 15449a747e4fSDavid du Colombier strcpy(d->name, name); 15459a747e4fSDavid du Colombier return d; 15469a747e4fSDavid du Colombier } 15479a747e4fSDavid du Colombier d->name = name; 15489a747e4fSDavid du Colombier return reallocdir(d, 1); 15499a747e4fSDavid du Colombier } 15509a747e4fSDavid du Colombier 15519a747e4fSDavid du Colombier Dir* 15529a747e4fSDavid du Colombier dir_change_uid(Dir *d, char *name) 15539a747e4fSDavid du Colombier { 15549a747e4fSDavid du Colombier if(d->uid && strlen(d->uid) >= strlen(name)){ 15559a747e4fSDavid du Colombier strcpy(d->name, name); 15569a747e4fSDavid du Colombier return d; 15579a747e4fSDavid du Colombier } 15589a747e4fSDavid du Colombier d->uid = name; 15599a747e4fSDavid du Colombier return reallocdir(d, 1); 15609a747e4fSDavid du Colombier } 15619a747e4fSDavid du Colombier 15629a747e4fSDavid du Colombier Dir* 15639a747e4fSDavid du Colombier dir_change_gid(Dir *d, char *name) 15649a747e4fSDavid du Colombier { 15659a747e4fSDavid du Colombier if(d->gid && strlen(d->gid) >= strlen(name)){ 15669a747e4fSDavid du Colombier strcpy(d->name, name); 15679a747e4fSDavid du Colombier return d; 15689a747e4fSDavid du Colombier } 15699a747e4fSDavid du Colombier d->gid = name; 15709a747e4fSDavid du Colombier return reallocdir(d, 1); 15719a747e4fSDavid du Colombier } 15729a747e4fSDavid du Colombier 15739a747e4fSDavid du Colombier Dir* 15749a747e4fSDavid du Colombier dir_change_muid(Dir *d, char *name) 15759a747e4fSDavid du Colombier { 15769a747e4fSDavid du Colombier if(d->muid && strlen(d->muid) >= strlen(name)){ 15779a747e4fSDavid du Colombier strcpy(d->name, name); 15789a747e4fSDavid du Colombier return d; 15799a747e4fSDavid du Colombier } 15809a747e4fSDavid du Colombier d->muid = name; 15819a747e4fSDavid du Colombier return reallocdir(d, 1); 15829a747e4fSDavid du Colombier } 1583ecc2a59cSDavid du Colombier 1584ecc2a59cSDavid du Colombier static int 1585ecc2a59cSDavid du Colombier nw_mode(char dirlet, char *s) /* NetWare file mode mapping */ 1586ecc2a59cSDavid du Colombier { 1587ecc2a59cSDavid du Colombier int mode = 0777; 1588ecc2a59cSDavid du Colombier 1589ecc2a59cSDavid du Colombier if(dirlet == 'd') 1590ecc2a59cSDavid du Colombier mode |= DMDIR; 1591ecc2a59cSDavid du Colombier 1592ecc2a59cSDavid du Colombier if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']') 1593ecc2a59cSDavid du Colombier return(mode); 1594ecc2a59cSDavid du Colombier 1595ecc2a59cSDavid du Colombier if (s[1] == '-') /* can't read file */ 1596ecc2a59cSDavid du Colombier mode &= ~0444; 1597ecc2a59cSDavid du Colombier if (dirlet == 'd' && s[6] == '-') /* cannot scan dir */ 1598ecc2a59cSDavid du Colombier mode &= ~0444; 1599ecc2a59cSDavid du Colombier if (s[2] == '-') /* can't write file */ 1600ecc2a59cSDavid du Colombier mode &= ~0222; 1601ecc2a59cSDavid du Colombier if (dirlet == 'd' && s[7] == '-' && s[3] == '-') /* cannot create in, or modify dir */ 1602ecc2a59cSDavid du Colombier mode &= ~0222; 1603ecc2a59cSDavid du Colombier 1604ecc2a59cSDavid du Colombier return(mode); 1605ecc2a59cSDavid du Colombier } 1606