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);
/*
* 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;
/*
* 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);
}
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;
/* 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;
/*
* 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;
/*
* 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);
}
/*
* 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];
/* 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");