1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657 |
- #include <u.h>
- #include <libc.h>
- #include <bio.h>
- #include <ip.h>
- #include <mp.h>
- #include <libsec.h>
- #include <auth.h>
- #include <fcall.h>
- #include <ctype.h>
- #include <String.h>
- #include "ftpfs.h"
- enum
- {
- /* return codes */
- Extra= 1,
- Success= 2,
- Incomplete= 3,
- TempFail= 4,
- PermFail= 5,
- Impossible= 6,
- };
- Node *remdir; /* current directory on remote machine */
- Node *remroot; /* root directory on remote machine */
- int ctlfd; /* fd for control connection */
- Biobuf ctlin; /* input buffer for control connection */
- Biobuf stdin; /* input buffer for standard input */
- Biobuf dbuf; /* buffer for data connection */
- char msg[512]; /* buffer for replies */
- char net[Maxpath]; /* network for connections */
- int listenfd; /* fd to listen on for connections */
- char netdir[Maxpath];
- int os, defos;
- char topsdir[64]; /* name of listed directory for TOPS */
- String *remrootpath; /* path on remote side to remote root */
- char *user;
- int nopassive;
- long lastsend;
- extern int usetls;
- static void sendrequest(char*, char*);
- static int getreply(Biobuf*, char*, int, int);
- static int active(int, Biobuf**, char*, char*);
- static int passive(int, Biobuf**, char*, char*);
- static int data(int, Biobuf**, char*, char*);
- static int port(void);
- static void ascii(void);
- static void image(void);
- static void unixpath(Node*, String*);
- static void vmspath(Node*, String*);
- static void mvspath(Node*, String*);
- static Node* vmsdir(char*);
- static int getpassword(char*, char*);
- static int nw_mode(char dirlet, char *s);
- /*
- * connect to remote server, default network is "tcp/ip"
- */
- void
- hello(char *dest)
- {
- char *p;
- char dir[Maxpath];
- TLSconn conn;
- Binit(&stdin, 0, OREAD); /* init for later use */
- ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0);
- if(ctlfd < 0){
- fprint(2, "can't dial %s: %r\n", dest);
- exits("dialing");
- }
-
- Binit(&ctlin, ctlfd, OREAD);
- /* remember network for the data connections */
- p = strrchr(dir+1, '/');
- if(p == 0)
- fatal("wrong dial(2) linked with ftp");
- *p = 0;
- safecpy(net, dir, sizeof(net));
- /* wait for hello from other side */
- if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
- fatal("bad hello");
- if(strstr(msg, "Plan 9"))
- os = Plan9;
- if(usetls){
- sendrequest("AUTH", "TLS");
- if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
- fatal("bad auth tls");
- ctlfd = tlsClient(ctlfd, &conn);
- if(ctlfd < 0)
- fatal("starting tls: %r");
- free(conn.cert);
- Binit(&ctlin, ctlfd, OREAD);
- sendrequest("PBSZ", "0");
- if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
- fatal("bad pbsz 0");
- sendrequest("PROT", "P");
- if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
- fatal("bad prot p");
- }
- }
- /*
- * login to remote system
- */
- void
- rlogin(char *rsys, char *keyspec)
- {
- char *line;
- char pass[128];
- UserPasswd *up;
- up = nil;
- for(;;){
- if(up == nil && os != Plan9)
- up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec);
- if(up != nil){
- sendrequest("USER", up->user);
- } else {
- print("User[default = %s]: ", user);
- line = Brdline(&stdin, '\n');
- if(line == 0)
- exits(0);
- line[Blinelen(&stdin)-1] = 0;
- if(*line){
- free(user);
- user = strdup(line);
- }
- sendrequest("USER", user);
- }
- switch(getreply(&ctlin, msg, sizeof(msg), 1)){
- case Success:
- goto out;
- case Incomplete:
- break;
- case TempFail:
- case PermFail:
- continue;
- }
- if(up != nil){
- sendrequest("PASS", up->passwd);
- } else {
- if(getpassword(pass, pass+sizeof(pass)) < 0)
- exits(0);
- sendrequest("PASS", pass);
- }
- if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){
- if(strstr(msg, "Sess#"))
- defos = MVS;
- break;
- }
- }
- out:
- if(up != nil){
- memset(up, 0, sizeof(*up));
- free(up);
- }
- }
- /*
- * login to remote system with given user name and password.
- */
- void
- clogin(char *cuser, char *cpassword)
- {
- free(user);
- user = strdup(cuser);
- if (strcmp(user, "anonymous") != 0 &&
- strcmp(user, "ftp") != 0)
- fatal("User must be 'anonymous' or 'ftp'");
- sendrequest("USER", user);
- switch(getreply(&ctlin, msg, sizeof(msg), 1)){
- case Success:
- return;
- case Incomplete:
- break;
- case TempFail:
- case PermFail:
- fatal("login failed");
- }
- if (cpassword == 0)
- fatal("password needed");
- sendrequest("PASS", cpassword);
- if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
- fatal("password failed");
- if(strstr(msg, "Sess#"))
- defos = MVS;
- return;
- }
- /*
- * find out about the other side. go to it's root if requested. set
- * image mode if a Plan9 system.
- */
- void
- preamble(char *mountroot)
- {
- char *p, *ep;
- int rv;
- OS *o;
- /*
- * create a root directory mirror
- */
- remroot = newnode(0, s_copy("/"));
- remroot->d->qid.type = QTDIR;
- remroot->d->mode = DMDIR|0777;
- remdir = remroot;
- /*
- * get system type
- */
- sendrequest("SYST", nil);
- switch(getreply(&ctlin, msg, sizeof(msg), 1)){
- case Success:
- for(o = oslist; o->os != Unknown; o++)
- if(strncmp(msg+4, o->name, strlen(o->name)) == 0)
- break;
- os = o->os;
- if(os == NT)
- os = Unix;
- break;
- default:
- os = defos;
- break;
- }
- if(os == Unknown)
- os = defos;
- remrootpath = s_reset(remrootpath);
- switch(os){
- case NetWare:
- /*
- * Request long, rather than 8.3 filenames,
- * where the Servers & Volume support them.
- */
- sendrequest("SITE LONG", nil);
- getreply(&ctlin, msg, sizeof(msg), 0);
- /* FALL THRU */
- case Unix:
- case Plan9:
- /*
- * go to the remote root, if asked
- */
- if(mountroot){
- sendrequest("CWD", mountroot);
- getreply(&ctlin, msg, sizeof(msg), 0);
- } else {
- s_append(remrootpath, "/usr/");
- s_append(remrootpath, user);
- }
- /*
- * get the root directory
- */
- sendrequest("PWD", nil);
- rv = getreply(&ctlin, msg, sizeof(msg), 1);
- if(rv == PermFail){
- sendrequest("XPWD", nil);
- rv = getreply(&ctlin, msg, sizeof(msg), 1);
- }
- if(rv == Success){
- p = strchr(msg, '"');
- if(p){
- p++;
- ep = strchr(p, '"');
- if(ep){
- *ep = 0;
- s_append(s_reset(remrootpath), p);
- }
- }
- }
- break;
- case Tops:
- case VM:
- /*
- * top directory is a figment of our imagination.
- * make it permanently cached & valid.
- */
- CACHED(remroot);
- VALID(remroot);
- remroot->d->atime = time(0) + 100000;
- /*
- * no initial directory. We are in the
- * imaginary root.
- */
- remdir = newtopsdir("???");
- topsdir[0] = 0;
- if(os == Tops && readdir(remdir) >= 0){
- CACHED(remdir);
- if(*topsdir)
- remdir->remname = s_copy(topsdir);
- VALID(remdir);
- }
- break;
- case VMS:
- /*
- * top directory is a figment of our imagination.
- * make it permanently cached & valid.
- */
- CACHED(remroot);
- VALID(remroot);
- remroot->d->atime = time(0) + 100000;
- /*
- * get current directory
- */
- sendrequest("PWD", nil);
- rv = getreply(&ctlin, msg, sizeof(msg), 1);
- if(rv == PermFail){
- sendrequest("XPWD", nil);
- rv = getreply(&ctlin, msg, sizeof(msg), 1);
- }
- if(rv == Success){
- p = strchr(msg, '"');
- if(p){
- p++;
- ep = strchr(p, '"');
- if(ep){
- *ep = 0;
- remroot = remdir = vmsdir(p);
- }
- }
- }
- break;
- case MVS:
- usenlst = 1;
- break;
- }
- if(os == Plan9)
- image();
- }
- static void
- ascii(void)
- {
- sendrequest("TYPE A", nil);
- switch(getreply(&ctlin, msg, sizeof(msg), 0)){
- case Success:
- break;
- default:
- fatal("can't set type to ascii");
- }
- }
- static void
- image(void)
- {
- sendrequest("TYPE I", nil);
- switch(getreply(&ctlin, msg, sizeof(msg), 0)){
- case Success:
- break;
- default:
- fatal("can't set type to image/binary");
- }
- }
- /*
- * decode the time fields, return seconds since epoch began
- */
- char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
- static Tm now;
- static ulong
- cracktime(char *month, char *day, char *yr, char *hms)
- {
- Tm tm;
- int i;
- char *p;
- /* default time */
- if(now.year == 0)
- now = *localtime(time(0));
- tm = now;
- tm.yday = 0;
- /* convert ascii month to a number twixt 1 and 12 */
- if(*month >= '0' && *month <= '9'){
- tm.mon = atoi(month) - 1;
- if(tm.mon < 0 || tm.mon > 11)
- tm.mon = 5;
- } else {
- for(p = month; *p; p++)
- *p = tolower(*p);
- for(i = 0; i < 12; i++)
- if(strncmp(&monthchars[i*3], month, 3) == 0){
- tm.mon = i;
- break;
- }
- }
- tm.mday = atoi(day);
- if(hms){
- tm.hour = strtol(hms, &p, 0);
- if(*p == ':'){
- tm.min = strtol(p+1, &p, 0);
- if(*p == ':')
- tm.sec = strtol(p+1, &p, 0);
- }
- if(tolower(*p) == 'p')
- tm.hour += 12;
- }
- if(yr){
- tm.year = atoi(yr);
- if(tm.year >= 1900)
- tm.year -= 1900;
- } else {
- if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
- tm.year--;
- }
- /* convert to epoch seconds */
- return tm2sec(&tm);
- }
- /*
- * decode a Unix or Plan 9 file mode
- */
- static ulong
- crackmode(char *p)
- {
- ulong flags;
- ulong mode;
- int i;
- flags = 0;
- switch(strlen(p)){
- case 10: /* unix and new style plan 9 */
- switch(*p){
- case 'l':
- return DMSYML|0777;
- case 'd':
- flags |= DMDIR;
- case 'a':
- flags |= DMAPPEND;
- }
- p++;
- if(p[2] == 'l')
- flags |= DMEXCL;
- break;
- case 11: /* old style plan 9 */
- switch(*p++){
- case 'd':
- flags |= DMDIR;
- break;
- case 'a':
- flags |= DMAPPEND;
- break;
- }
- if(*p++ == 'l')
- flags |= DMEXCL;
- break;
- default:
- return DMDIR|0777;
- }
- mode = 0;
- for(i = 0; i < 3; i++){
- mode <<= 3;
- if(*p++ == 'r')
- mode |= DMREAD;
- if(*p++ == 'w')
- mode |= DMWRITE;
- if(*p == 'x' || *p == 's' || *p == 'S')
- mode |= DMEXEC;
- p++;
- }
- return mode | flags;
- }
- /*
- * find first punctuation
- */
- char*
- strpunct(char *p)
- {
- int c;
- for(;c = *p; p++){
- if(ispunct(c))
- return p;
- }
- return 0;
- }
- /*
- * decode a Unix or Plan 9 directory listing
- */
- static Dir*
- crackdir(char *p, String **remname)
- {
- char *field[15];
- char *dfield[4];
- char *cp;
- String *s;
- int dn, n;
- Dir d, *dp;
- memset(&d, 0, sizeof(d));
- n = getfields(p, field, 15, 1, " \t");
- if(n > 2 && strcmp(field[n-2], "->") == 0)
- n -= 2;
- switch(os){
- case TSO:
- cp = strchr(field[0], '.');
- if(cp){
- *cp++ = 0;
- s = s_copy(cp);
- d.uid = field[0];
- } else {
- s = s_copy(field[0]);
- d.uid = "TSO";
- }
- d.gid = "TSO";
- d.mode = 0666;
- d.length = 0;
- d.atime = 0;
- break;
- case OS½:
- s = s_copy(field[n-1]);
- d.uid = "OS½";
- d.gid = d.uid;
- d.mode = 0666;
- switch(n){
- case 5:
- if(strcmp(field[1], "DIR") == 0)
- d.mode |= DMDIR;
- d.length = atoi(field[0]);
- dn = getfields(field[2], dfield, 4, 1, "-");
- if(dn == 3)
- d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]);
- break;
- }
- break;
- case Tops:
- if(n != 4){ /* tops directory name */
- safecpy(topsdir, field[0], sizeof(topsdir));
- return 0;
- }
- s = s_copy(field[3]);
- d.length = atoi(field[0]);
- d.mode = 0666;
- d.uid = "Tops";
- d.gid = d.uid;
- dn = getfields(field[1], dfield, 4, 1, "-");
- if(dn == 3)
- d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]);
- else
- d.atime = time(0);
- break;
- case VM:
- switch(n){
- case 9:
- s = s_copy(field[0]);
- s_append(s, ".");
- s_append(s, field[1]);
- d.length = atoi(field[3])*atoi(field[4]);
- if(*field[2] == 'F')
- d.mode = 0666;
- else
- d.mode = 0777;
- d.uid = "VM";
- d.gid = d.uid;
- dn = getfields(field[6], dfield, 4, 1, "/-");
- if(dn == 3)
- d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]);
- else
- d.atime = time(0);
- break;
- case 1:
- s = s_copy(field[0]);
- d.uid = "VM";
- d.gid = d.uid;
- d.mode = 0777;
- d.atime = 0;
- break;
- default:
- return nil;
- }
- break;
- case VMS:
- switch(n){
- case 6:
- for(cp = field[0]; *cp; cp++)
- *cp = tolower(*cp);
- cp = strchr(field[0], ';');
- if(cp)
- *cp = 0;
- d.mode = 0666;
- cp = field[0] + strlen(field[0]) - 4;
- if(strcmp(cp, ".dir") == 0){
- d.mode |= DMDIR;
- *cp = 0;
- }
- s = s_copy(field[0]);
- d.length = atoi(field[1])*512;
- field[4][strlen(field[4])-1] = 0;
- d.uid = field[4]+1;
- d.gid = d.uid;
- dn = getfields(field[2], dfield, 4, 1, "/-");
- if(dn == 3)
- d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]);
- else
- d.atime = time(0);
- break;
- default:
- return nil;
- }
- break;
- case NetWare:
- switch(n){
- case 8: /* New style */
- s = s_copy(field[7]);
- d.uid = field[2];
- d.gid = d.uid;
- d.mode = nw_mode(field[0][0], field[1]);
- d.length = atoi(field[3]);
- if(strchr(field[6], ':'))
- d.atime = cracktime(field[4], field[5], nil, field[6]);
- else
- d.atime = cracktime(field[4], field[5], field[6], nil);
- break;
- case 9:
- s = s_copy(field[8]);
- d.uid = field[2];
- d.gid = d.uid;
- d.mode = 0666;
- if(*field[0] == 'd')
- d.mode |= DMDIR;
- d.length = atoi(field[3]);
- d.atime = cracktime(field[4], field[5], field[6], field[7]);
- break;
- case 1:
- s = s_copy(field[0]);
- d.uid = "none";
- d.gid = d.uid;
- d.mode = 0777;
- d.atime = 0;
- break;
- default:
- return nil;
- }
- break;
- case Unix:
- case Plan9:
- default:
- switch(n){
- case 8: /* ls -l */
- s = s_copy(field[7]);
- d.uid = field[2];
- d.gid = d.uid;
- d.mode = crackmode(field[0]);
- d.length = atoi(field[3]);
- if(strchr(field[6], ':'))
- d.atime = cracktime(field[4], field[5], 0, field[6]);
- else
- d.atime = cracktime(field[4], field[5], field[6], 0);
- break;
- case 9: /* ls -lg */
- s = s_copy(field[8]);
- d.uid = field[2];
- d.gid = field[3];
- d.mode = crackmode(field[0]);
- d.length = atoi(field[4]);
- if(strchr(field[7], ':'))
- d.atime = cracktime(field[5], field[6], 0, field[7]);
- else
- d.atime = cracktime(field[5], field[6], field[7], 0);
- break;
- case 10: /* plan 9 */
- s = s_copy(field[9]);
- d.uid = field[3];
- d.gid = field[4];
- d.mode = crackmode(field[0]);
- d.length = atoi(field[5]);
- if(strchr(field[8], ':'))
- d.atime = cracktime(field[6], field[7], 0, field[8]);
- else
- d.atime = cracktime(field[6], field[7], field[8], 0);
- break;
- case 4: /* a Windows_NT version */
- s = s_copy(field[3]);
- d.uid = "NT";
- d.gid = d.uid;
- if(strcmp("<DIR>", field[2]) == 0){
- d.length = 0;
- d.mode = DMDIR|0777;
- } else {
- d.mode = 0666;
- d.length = atoi(field[2]);
- }
- dn = getfields(field[0], dfield, 4, 1, "/-");
- if(dn == 3)
- d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]);
- break;
- case 1:
- s = s_copy(field[0]);
- d.uid = "none";
- d.gid = d.uid;
- d.mode = 0777;
- d.atime = 0;
- break;
- default:
- return nil;
- }
- }
- d.muid = d.uid;
- d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE;
- d.mtime = d.atime;
- if(ext && (d.qid.type & QTDIR) == 0)
- s_append(s, ext);
- d.name = s_to_c(s);
- /* allocate a freeable dir structure */
- dp = reallocdir(&d, 0);
- *remname = s;
- return dp;
- }
- /*
- * probe files in a directory to see if they are directories
- */
- /*
- * read a remote directory
- */
- int
- readdir(Node *node)
- {
- Biobuf *bp;
- char *line;
- Node *np;
- Dir *d;
- long n;
- int tries, x, files;
- static int uselist;
- int usenlist;
- String *remname;
- if(changedir(node) < 0)
- return -1;
- usenlist = 0;
- for(tries = 0; tries < 3; tries++){
- if(usenlist || usenlst)
- x = data(OREAD, &bp, "NLST", nil);
- else if(os == Unix && !uselist)
- x = data(OREAD, &bp, "LIST -l", nil);
- else
- x = data(OREAD, &bp, "LIST", nil);
- switch(x){
- case Extra:
- break;
- /* case TempFail:
- continue;
- */
- default:
- if(os == Unix && uselist == 0){
- uselist = 1;
- continue;
- }
- return seterr(nosuchfile);
- }
- files = 0;
- while(line = Brdline(bp, '\n')){
- n = Blinelen(bp);
- if(debug)
- write(2, line, n);
- if(n > 1 && line[n-2] == '\r')
- n--;
- line[n - 1] = 0;
- d = crackdir(line, &remname);
- if(d == nil)
- continue;
- files++;
- np = extendpath(node, remname);
- d->qid.path = np->d->qid.path;
- d->qid.vers = np->d->qid.vers;
- d->type = np->d->type;
- d->dev = 1; /* mark node as valid */
- if(os == MVS && node == remroot){
- d->qid.type = QTDIR;
- d->mode |= DMDIR;
- }
- free(np->d);
- np->d = d;
- }
- close(Bfildes(bp));
- switch(getreply(&ctlin, msg, sizeof(msg), 0)){
- case Success:
- if(files == 0 && !usenlst && !usenlist){
- usenlist = 1;
- continue;
- }
- if(files && usenlist)
- usenlst = 1;
- if(usenlst)
- node->chdirunknown = 1;
- return 0;
- case TempFail:
- break;
- default:
- return seterr(nosuchfile);
- }
- }
- return seterr(nosuchfile);
- }
- /*
- * create a remote directory
- */
- int
- createdir(Node *node)
- {
- if(changedir(node->parent) < 0)
- return -1;
-
- sendrequest("MKD", node->d->name);
- if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
- return -1;
- return 0;
- }
- /*
- * change to a remote directory.
- */
- int
- changedir(Node *node)
- {
- Node *to;
- String *cdpath;
- to = node;
- if(to == remdir)
- return 0;
- /* build an absolute path */
- switch(os){
- case Tops:
- case VM:
- switch(node->depth){
- case 0:
- remdir = node;
- return 0;
- case 1:
- cdpath = s_clone(node->remname);
- break;
- default:
- return seterr(nosuchfile);
- }
- break;
- case VMS:
- switch(node->depth){
- case 0:
- remdir = node;
- return 0;
- default:
- cdpath = s_new();
- vmspath(node, cdpath);
- }
- break;
- case MVS:
- if(node->depth == 0)
- cdpath = s_clone(remrootpath);
- else{
- cdpath = s_new();
- mvspath(node, cdpath);
- }
- break;
- default:
- if(node->depth == 0)
- cdpath = s_clone(remrootpath);
- else{
- cdpath = s_new();
- unixpath(node, cdpath);
- }
- break;
- }
- uncachedir(remdir, 0);
- /*
- * connect, if we need a password (Incomplete)
- * act like it worked (best we can do).
- */
- sendrequest("CWD", s_to_c(cdpath));
- s_free(cdpath);
- switch(getreply(&ctlin, msg, sizeof(msg), 0)){
- case Success:
- case Incomplete:
- remdir = node;
- return 0;
- default:
- return seterr(nosuchfile);
- }
- }
- /*
- * read a remote file
- */
- int
- readfile1(Node *node)
- {
- Biobuf *bp;
- char buf[4*1024];
- long off;
- int n;
- int tries;
- if(changedir(node->parent) < 0)
- return -1;
- for(tries = 0; tries < 4; tries++){
- switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){
- case Extra:
- break;
- case TempFail:
- continue;
- default:
- return seterr(nosuchfile);
- }
- off = 0;
- while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){
- if(filewrite(node, buf, off, n) != n){
- off = -1;
- break;
- }
- off += n;
- }
- if(off < 0)
- return -1;
- /* make sure a file gets created even for a zero length file */
- if(off == 0)
- filewrite(node, buf, 0, 0);
- close(Bfildes(bp));
- switch(getreply(&ctlin, msg, sizeof(msg), 0)){
- case Success:
- return off;
- case TempFail:
- continue;
- default:
- return seterr(nosuchfile);
- }
- }
- return seterr(nosuchfile);
- }
- int
- readfile(Node *node)
- {
- int rv, inimage;
- switch(os){
- case MVS:
- case Plan9:
- case Tops:
- case TSO:
- inimage = 0;
- break;
- default:
- inimage = 1;
- image();
- break;
- }
- rv = readfile1(node);
- if(inimage)
- ascii();
- return rv;
- }
- /*
- * write back a file
- */
- int
- createfile1(Node *node)
- {
- Biobuf *bp;
- char buf[4*1024];
- long off;
- int n;
- if(changedir(node->parent) < 0)
- return -1;
- if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra)
- return -1;
- for(off = 0; ; off += n){
- n = fileread(node, buf, off, sizeof(buf));
- if(n <= 0)
- break;
- write(Bfildes(bp), buf, n);
- }
- close(Bfildes(bp));
- if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
- return -1;
- return off;
- }
- int
- createfile(Node *node)
- {
- int rv;
- switch(os){
- case Plan9:
- case Tops:
- break;
- default:
- image();
- break;
- }
- rv = createfile1(node);
- switch(os){
- case Plan9:
- case Tops:
- break;
- default:
- ascii();
- break;
- }
- return rv;
- }
- /*
- * remove a remote file
- */
- int
- removefile(Node *node)
- {
- if(changedir(node->parent) < 0)
- return -1;
-
- sendrequest("DELE", s_to_c(node->remname));
- if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
- return -1;
- return 0;
- }
- /*
- * remove a remote directory
- */
- int
- removedir(Node *node)
- {
- if(changedir(node->parent) < 0)
- return -1;
-
- sendrequest("RMD", s_to_c(node->remname));
- if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
- return -1;
- return 0;
- }
- /*
- * tell remote that we're exiting and then do it
- */
- void
- quit(void)
- {
- sendrequest("QUIT", nil);
- getreply(&ctlin, msg, sizeof(msg), 0);
- exits(0);
- }
- /*
- * send a request
- */
- static void
- sendrequest(char *a, char *b)
- {
- char buf[2*1024];
- int n;
- n = strlen(a)+2+1;
- if(b != nil)
- n += strlen(b)+1;
- if(n >= sizeof(buf))
- fatal("proto request too long");
- strcpy(buf, a);
- if(b != nil){
- strcat(buf, " ");
- strcat(buf, b);
- }
- strcat(buf, "\r\n");
- n = strlen(buf);
- if(write(ctlfd, buf, n) != n)
- fatal("remote side hung up");
- if(debug)
- write(2, buf, n);
- lastsend = time(0);
- }
- /*
- * replies codes are in the range [100, 999] and may contain multiple lines of
- * continuation.
- */
- static int
- getreply(Biobuf *bp, char *msg, int len, int printreply)
- {
- char *line;
- char *p;
- int rv;
- int i, n;
- while(line = Brdline(bp, '\n')){
- /* add line to message buffer, strip off \r */
- n = Blinelen(bp);
- if(n > 1 && line[n-2] == '\r'){
- n--;
- line[n-1] = '\n';
- }
- if(printreply && !quiet)
- write(1, line, n);
- else if(debug)
- write(2, line, n);
- if(n > len - 1)
- i = len - 1;
- else
- i = n;
- if(i > 0){
- memmove(msg, line, i);
- msg += i;
- len -= i;
- *msg = 0;
- }
- /* stop if not a continuation */
- rv = strtol(line, &p, 10);
- if(rv >= 100 && rv < 600 && p==line+3 && *p != '-')
- return rv/100;
- /* tell user about continuations */
- if(!debug && !quiet && !printreply)
- write(2, line, n);
- }
- fatal("remote side closed connection");
- return 0;
- }
- /*
- * Announce on a local port and tell its address to the the remote side
- */
- static int
- port(void)
- {
- char buf[256];
- int n, fd;
- char *ptr;
- uchar ipaddr[IPaddrlen];
- int port;
- /* get a channel to listen on, let kernel pick the port number */
- sprint(buf, "%s!*!0", net);
- listenfd = announce(buf, netdir);
- if(listenfd < 0)
- return seterr("can't announce");
- /* get the local address and port number */
- sprint(buf, "%s/local", netdir);
- fd = open(buf, OREAD);
- if(fd < 0)
- return seterr("opening %s: %r", buf);
- n = read(fd, buf, sizeof(buf)-1);
- close(fd);
- if(n <= 0)
- return seterr("opening %s/local: %r", netdir);
- buf[n] = 0;
- ptr = strchr(buf, ' ');
- if(ptr)
- *ptr = 0;
- ptr = strchr(buf, '!')+1;
- port = atoi(ptr);
- memset(ipaddr, 0, IPaddrlen);
- if (*net){
- strcpy(buf, net);
- ptr = strchr(buf +1, '/');
- if (ptr)
- *ptr = 0;
- myipaddr(ipaddr, buf);
- }
- /* tell remote side */
- sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1],
- ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff);
- sendrequest(buf, nil);
- if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
- return seterr(msg);
- return 0;
- }
- /*
- * have server call back for a data connection
- */
- static int
- active(int mode, Biobuf **bpp, char *cmda, char *cmdb)
- {
- int cfd, dfd, rv;
- char newdir[Maxpath];
- char datafile[Maxpath + 6];
- TLSconn conn;
- if(port() < 0)
- return TempFail;
- sendrequest(cmda, cmdb);
- rv = getreply(&ctlin, msg, sizeof(msg), 0);
- if(rv != Extra){
- close(listenfd);
- return rv;
- }
- /* wait for a new call */
- cfd = listen(netdir, newdir);
- if(cfd < 0)
- fatal("waiting for data connection");
- close(listenfd);
- /* open it's data connection and close the control connection */
- sprint(datafile, "%s/data", newdir);
- dfd = open(datafile, ORDWR);
- close(cfd);
- if(dfd < 0)
- fatal("opening data connection");
- if(usetls){
- memset(&conn, 0, sizeof(conn));
- dfd = tlsClient(dfd, &conn);
- if(dfd < 0)
- fatal("starting tls: %r");
- free(conn.cert);
- }
- Binit(&dbuf, dfd, mode);
- *bpp = &dbuf;
- return Extra;
- }
- /*
- * call out for a data connection
- */
- static int
- passive(int mode, Biobuf **bpp, char *cmda, char *cmdb)
- {
- char msg[1024];
- char ds[1024];
- char *f[6];
- char *p;
- int x, fd;
- TLSconn conn;
- if(nopassive)
- return Impossible;
- sendrequest("PASV", nil);
- if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){
- nopassive = 1;
- return Impossible;
- }
- /* get address and port number from reply, this is AI */
- p = strchr(msg, '(');
- if(p == 0){
- for(p = msg+3; *p; p++)
- if(isdigit(*p))
- break;
- } else
- p++;
- if(getfields(p, f, 6, 0, ",") < 6){
- if(debug)
- fprint(2, "passive mode protocol botch: %s\n", msg);
- werrstr("ftp protocol botch");
- nopassive = 1;
- return Impossible;
- }
- snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net,
- f[0], f[1], f[2], f[3],
- ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
- /* open data connection */
- fd = dial(ds, 0, 0, 0);
- if(fd < 0){
- if(debug)
- fprint(2, "passive mode connect to %s failed: %r\n", ds);
- nopassive = 1;
- return TempFail;
- }
- /* tell remote to send a file */
- sendrequest(cmda, cmdb);
- x = getreply(&ctlin, msg, sizeof(msg), 0);
- if(x != Extra){
- close(fd);
- if(debug)
- fprint(2, "passive mode retrieve failed: %s\n", msg);
- werrstr(msg);
- return x;
- }
- if(usetls){
- memset(&conn, 0, sizeof(conn));
- fd = tlsClient(fd, &conn);
- if(fd < 0)
- fatal("starting tls: %r");
- free(conn.cert);
- }
- Binit(&dbuf, fd, mode);
- *bpp = &dbuf;
- return Extra;
- }
- static int
- data(int mode, Biobuf **bpp, char* cmda, char *cmdb)
- {
- int x;
- x = passive(mode, bpp, cmda, cmdb);
- if(x != Impossible)
- return x;
- return active(mode, bpp, cmda, cmdb);
- }
- /*
- * used for keep alives
- */
- void
- nop(void)
- {
- if(lastsend - time(0) < 15)
- return;
- sendrequest("PWD", nil);
- getreply(&ctlin, msg, sizeof(msg), 0);
- }
- /*
- * turn a vms spec into a path
- */
- static Node*
- vmsextendpath(Node *np, char *name)
- {
- np = extendpath(np, s_copy(name));
- if(!ISVALID(np)){
- np->d->qid.type = QTDIR;
- np->d->atime = time(0);
- np->d->mtime = np->d->atime;
- strcpy(np->d->uid, "who");
- strcpy(np->d->gid, "cares");
- np->d->mode = DMDIR|0777;
- np->d->length = 0;
- if(changedir(np) >= 0)
- VALID(np);
- }
- return np;
- }
- static Node*
- vmsdir(char *name)
- {
- char *cp;
- Node *np;
- char *oname;
- np = remroot;
- cp = strchr(name, '[');
- if(cp)
- strcpy(cp, cp+1);
- cp = strchr(name, ']');
- if(cp)
- *cp = 0;
- oname = name = strdup(name);
- if(name == 0)
- return 0;
- while(cp = strchr(name, '.')){
- *cp = 0;
- np = vmsextendpath(np, name);
- name = cp+1;
- }
- np = vmsextendpath(np, name);
- /*
- * walk back to first accessible directory
- */
- for(; np->parent != np; np = np->parent)
- if(ISVALID(np)){
- CACHED(np->parent);
- break;
- }
- free(oname);
- return np;
- }
- /*
- * walk up the tree building a VMS style path
- */
- static void
- vmspath(Node *node, String *path)
- {
- char *p;
- int n;
- if(node->depth == 1){
- p = strchr(s_to_c(node->remname), ':');
- if(p){
- n = p - s_to_c(node->remname) + 1;
- s_nappend(path, s_to_c(node->remname), n);
- s_append(path, "[");
- s_append(path, p+1);
- } else {
- s_append(path, "[");
- s_append(path, s_to_c(node->remname));
- }
- s_append(path, "]");
- return;
- }
- vmspath(node->parent, path);
- s_append(path, ".");
- s_append(path, s_to_c(node->remname));
- }
- /*
- * walk up the tree building a Unix style path
- */
- static void
- unixpath(Node *node, String *path)
- {
- if(node == node->parent){
- s_append(path, s_to_c(remrootpath));
- return;
- }
- unixpath(node->parent, path);
- if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0)
- s_append(path, "/");
- s_append(path, s_to_c(node->remname));
- }
- /*
- * walk up the tree building a MVS style path
- */
- static void
- mvspath(Node *node, String *path)
- {
- if(node == node->parent){
- s_append(path, s_to_c(remrootpath));
- return;
- }
- mvspath(node->parent, path);
- if(s_len(path) > 0)
- s_append(path, ".");
- s_append(path, s_to_c(node->remname));
- }
- static int
- getpassword(char *buf, char *e)
- {
- char *p;
- int c;
- int consctl, rv = 0;
- consctl = open("/dev/consctl", OWRITE);
- if(consctl >= 0)
- write(consctl, "rawon", 5);
- print("Password: ");
- e--;
- for(p = buf; p <= e; p++){
- c = Bgetc(&stdin);
- if(c < 0){
- rv = -1;
- goto out;
- }
- if(c == '\n' || c == '\r')
- break;
- *p = c;
- }
- *p = 0;
- print("\n");
- out:
- if(consctl >= 0)
- close(consctl);
- return rv;
- }
- /*
- * convert from latin1 to utf
- */
- static char*
- fromlatin1(char *from)
- {
- char *p, *to;
- Rune r;
- if(os == Plan9)
- return nil;
- /* don't convert if we don't have to */
- for(p = from; *p; p++)
- if(*p & 0x80)
- break;
- if(*p == 0)
- return nil;
- to = malloc(3*strlen(from)+2);
- if(to == nil)
- return nil;
- for(p = to; *from; from++){
- r = (*from) & 0xff;
- p += runetochar(p, &r);
- }
- *p = 0;
- return to;
- }
- Dir*
- reallocdir(Dir *d, int dofree)
- {
- Dir *dp;
- char *p;
- int nn, ng, nu, nm;
- char *utf;
- if(d->name == nil)
- d->name = "?name?";
- if(d->uid == nil)
- d->uid = "?uid?";
- if(d->gid == nil)
- d->gid = d->uid;
- if(d->muid == nil)
- d->muid = d->uid;
-
- utf = fromlatin1(d->name);
- if(utf != nil)
- d->name = utf;
- nn = strlen(d->name)+1;
- nu = strlen(d->uid)+1;
- ng = strlen(d->gid)+1;
- nm = strlen(d->muid)+1;
- dp = malloc(sizeof(Dir)+nn+nu+ng+nm);
- if(dp == nil){
- if(dofree)
- free(d);
- if(utf != nil)
- free(utf);
- return nil;
- }
- *dp = *d;
- p = (char*)&dp[1];
- strcpy(p, d->name);
- dp->name = p;
- p += nn;
- strcpy(p, d->uid);
- dp->uid = p;
- p += nu;
- strcpy(p, d->gid);
- dp->gid = p;
- p += ng;
- strcpy(p, d->muid);
- dp->muid = p;
- if(dofree)
- free(d);
- if(utf != nil)
- free(utf);
- return dp;
- }
- Dir*
- dir_change_name(Dir *d, char *name)
- {
- if(d->name && strlen(d->name) >= strlen(name)){
- strcpy(d->name, name);
- return d;
- }
- d->name = name;
- return reallocdir(d, 1);
- }
- Dir*
- dir_change_uid(Dir *d, char *name)
- {
- if(d->uid && strlen(d->uid) >= strlen(name)){
- strcpy(d->name, name);
- return d;
- }
- d->uid = name;
- return reallocdir(d, 1);
- }
- Dir*
- dir_change_gid(Dir *d, char *name)
- {
- if(d->gid && strlen(d->gid) >= strlen(name)){
- strcpy(d->name, name);
- return d;
- }
- d->gid = name;
- return reallocdir(d, 1);
- }
- Dir*
- dir_change_muid(Dir *d, char *name)
- {
- if(d->muid && strlen(d->muid) >= strlen(name)){
- strcpy(d->name, name);
- return d;
- }
- d->muid = name;
- return reallocdir(d, 1);
- }
- static int
- nw_mode(char dirlet, char *s) /* NetWare file mode mapping */
- {
- int mode = 0777;
- if(dirlet == 'd')
- mode |= DMDIR;
- if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']')
- return(mode);
- if (s[1] == '-') /* can't read file */
- mode &= ~0444;
- if (dirlet == 'd' && s[6] == '-') /* cannot scan dir */
- mode &= ~0444;
- if (s[2] == '-') /* can't write file */
- mode &= ~0222;
- if (dirlet == 'd' && s[7] == '-' && s[3] == '-') /* cannot create in, or modify dir */
- mode &= ~0222;
- return(mode);
- }
|