/*********************************************************************\
* Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu) *
* Copyright (c) 1992 by Phil Richards (pgr@prg.ox.ac.uk) *
* *
* You may copy or modify this file in any manner you wish, provided *
* that this notice is always included, and that you hold the author *
* harmless for any loss or damage resulting from the installation or *
* use of this software. *
\*********************************************************************/
#include "client.h"
#include <setjmp.h>
#include <stdarg.h>
#include <stdlib.h>
char *env_dir = (char*)0;
char *env_myport = (char*)0;
char *env_host = (char*)0;
char *env_port = (char*)0;
int standalone = 1;
u_short client_buf_len = UBUF_SPACE;
u_short client_net_len;
int readme_max = -1;
static int oldserver = 0;
static int util_get_version(void);
/* version cache */
static char *version = 0;
static unsigned char *verbmap = 0;
static u_int verbmaplen = 0;
static int version_error = 0;
static u_long thruput_limit = 0;
static u_int packet_limit = 0;
void
ffprintf(FILE *out, const char *fmt, ...)
{
if (out)
{
va_list args;
va_start(args, fmt);
(void)vfprintf(out, fmt, args);
va_end(args);
(void)fflush(out);
}
}
static jmp_buf my_fgets_env;
static RETSIGTYPE
my_fgets_intr(int sig)
{
ffprintf(STDPROMPT,"\n");
longjmp(my_fgets_env, 0);
}
char *
my_fgets(char *s, int n, FILE *fs)
{
RETSIGTYPE (*oldintr)(int);
int resetintr = 0;
char *retval;
if (!setjmp(my_fgets_env))
{
oldintr = signal(SIGINT, my_fgets_intr);
resetintr = 1;
retval = fgets(s, n, fs);
}
else
{
*s = '\0';
retval = s;
}
if (resetintr)
(void)signal(SIGINT, oldintr);
return retval;
}
/* rewritten completely by pgr */
char *
util_abs_path(const char *buf)
{
char *dstp, *srcp, *slash, *path;
if (env_dir == 0)
env_dir = strdup("/");
if (buf == 0)
buf = "";
if (buf[0] == '/')
{
path = (char *)malloc(strlen(buf) + 2);
(void)sprintf(path, "%s/", buf);
}
else
{
path = (char *)malloc(strlen(env_dir) + strlen(buf) + 4);
(void)sprintf(path, "/%s/%s/", env_dir, buf);
}
for (slash = path, dstp = srcp = path + 1; *srcp; srcp++)
{
if (*srcp == '/')
{
if (dstp == slash + 1) /* "//" */
continue;
if (dstp == slash + 2 && slash[1] == '.')
{ /* "/./" */
dstp = slash + 1;
continue;
}
if (dstp == slash + 3 && slash[1] == '.' && slash[2] == '.')
{ /* "/../" */
if (slash != path)
while (*(--slash) != '/')
;
dstp = slash + 1;
continue;
}
slash = dstp;
}
*(dstp++) = *srcp;
}
if (slash != path)
*slash = '\0';
else
*dstp = '\0';
/* this returns a path with a "/" at the beginning */
return path;
}
char *
util_getwd(char *p)
{
if (p)
(void)strcpy(p, env_dir);
return(p);
}
RDIRENT **
get_dir_blk(char *path)
{
RDIRENT **dp = (RDIRENT **)0;
char *p1, *p2, *fpath, buf[2*UBUF_MAXSPACE];
u_long pos;
int cnt, k, len, rem, acc, at_eof;
UBUF *ub;
unsigned short blocksize;
if (!validate_operation(path, LITERAL_DIR | DIR_LIST))
return 0;
fpath = util_abs_path(path);
for (pos = 0, blocksize = 0, at_eof = acc = cnt = 0; ; )
{
while ((acc < UBUF_MAXSPACE) && !at_eof)
{
ub = client_interact(CC_GET_DIR, pos,
strlen(fpath), fpath+1,
2, (char *)&client_net_len);
if (client_intr_state > 1 || ub == 0)
{
if (dp)
(void)free((char*)dp);
(void)free(fpath);
return((RDIRENT **) 0);
}
if(ub->cmd == CC_ERR)
{
if (dp)
(void)free((char*)dp);
(void)free(fpath);
errno = EACCES;
return((RDIRENT **) 0);
}
if (blocksize == 0)
blocksize = ub->len;
else
if (ub->len < blocksize)
at_eof = 1;
/*
for (p1 = ub->buf, p2 = buf + acc, k = ub->len; k--; )
*p2++ = *p1++;
*/
memcpy(buf + acc, ub->buf, ub->len);
acc += ub->len;
pos += ub->len;
}
len = (acc >= blocksize)? blocksize: acc;
for (p2 = buf, rem = len, k = 0; ; k++)
{
if (rem < RDHSIZE)
break;
if (((RDIRENT *) p2)->type == RDTYPE_SKIP)
break;
if (((RDIRENT *) p2)->type == RDTYPE_END )
{
k++;
break;
}
p2 += RDHSIZE;
rem -= (RDHSIZE+1);
while (*p2++)
rem--;
while ((p2 - buf) & 3)
{
p2++;
rem--;
}
}
if (cnt)
dp = (RDIRENT**)realloc((char*)dp, (cnt+k+1) * sizeof(RDIRENT*));
else
dp = (RDIRENT**)malloc((cnt+k+1) * sizeof(RDIRENT*));
if (!dp)
{
(void)free(fpath);
ffprintf(STDERR, "?directory reading out of memory\n");
return (RDIRENT**)0;
}
for (p2 = buf, rem = len; ; cnt++)
{
RDIRENT *p2p;
int l;
if (rem < RDHSIZE)
break;
p2p = (RDIRENT*)p2;
if (p2p->type == RDTYPE_SKIP)
break;
if (p2p->type == RDTYPE_END)
{
(void)free(fpath);
dp[cnt] = 0;
return dp;
}
l = strlen(p2p->name);
dp[cnt] = (RDIRENT*)malloc(RDHSIZE + l + 1);
dp[cnt]->time = ntohl(p2p->time);
dp[cnt]->size = ntohl(p2p->size);
dp[cnt]->type = p2p->type;
(void)strcpy(dp[cnt]->name, p2p->name);
p2 += RDHSIZE + l + 1;
rem -= RDHSIZE + l + 1;
while ((p2 - buf) & 3)
{
p2++;
rem--;
}
}
if (acc < blocksize)
{
(void)free(fpath);
dp[cnt] = 0;
return dp;
}
for (p1 = buf + blocksize, p2 = buf, k = (acc -= blocksize); k--; )
*p2++ = *p1++;
}
}
int
util_download_main(char *path, char *fpath,
int fd,
int cmd, u_long offset, u_long length)
{
u_long pos;
u_int sent_time;
UBUF *ub;
struct timeval starttime;
struct stat rfstat;
int retval = 0;
if (!askprompt && client_intr_state > 1)
return 0;
if (util_stat(fpath, &rfstat) < 0)
{
ffprintf(STDERR, "?remote file `%s': %s\n", fpath, strerror(errno));
return -1;
}
if (offset > rfstat.st_size || length <= 0)
return 0;
(void)gettimeofday(&starttime, (struct timezone *)0);
for (pos = offset, sent_time = 0;
client_intr_state < 2 && retval == 0 && pos - offset < length;
)
{
int wrote;
ub = client_interact(cmd, pos, strlen(fpath), fpath+1,
2, (char *)&client_net_len);
if (client_intr_state > 1 || ub == 0 || ub->cmd == CC_ERR)
break;
if (client_trace == 1 && (udp_sent_time != sent_time))
{
sent_time = udp_sent_time;
if (client_buf_len >= UBUF_SPACE)
ffprintf(STDINFO,"\r%luk ", 1 + (pos>>10));
else
ffprintf(STDINFO,"\r%lu ", pos);
}
else if (client_trace >= 2)
{
(void)fputc('#', STDINFO);
(void)fflush(STDINFO);
}
wrote = write(fd, ub->buf, ub->len);
if (wrote < 0)
{
ffprintf(STDERR, "?download: cannot write to local file: %s\n",
strerror(errno));
retval = -1;
break;
}
else if (wrote == 0)
break;
pos += wrote;
if (wrote < ub->len)
break;
}
if (retval == 0)
retval = pos - offset;
if (client_trace)
{
struct timeval endtime;
int millicnt;
(void)gettimeofday(&endtime, (struct timezone *)0);
millicnt = 1000 * (endtime.tv_sec - starttime.tv_sec) +
(endtime.tv_usec - starttime.tv_usec) / 1000;
ffprintf(STDINFO,
"%cdone %lu bytes in %3.01f seconds (%3.02f K/sec)\n",
(client_trace == 1 && retval < 0)? '\r': '\n',
(u_long)(pos - offset),
(double)millicnt / 1000.0,
millicnt == 0? (double)(pos - offset)/1024.0
:((double)(pos - offset)/1024.0) /
((double)millicnt/1000.0));
if (client_trace == 3)
print_comm_stats(STDERR);
(void)fflush(STDERR);
}
return retval;
}
int
util_download(char *path, int fd, u_long offset, u_long length)
{
int bytes;
char *fpath;
fpath = util_abs_path(path);
bytes = util_download_main(path, fpath, fd, CC_GET_FILE, offset, length);
(void)free(fpath);
return bytes;
}
int
util_grab_file(char *path, int fd, u_long offset, u_long length)
{
u_long bytes;
char *fpath;
UBUF *ub;
fpath = util_abs_path(path);
bytes = util_download_main(path, fpath, fd, CC_GRAB_FILE, offset, length);
if (bytes != length)
{
(void)free(fpath);
return -1;
}
ub = client_interact(CC_GRAB_DONE, 0L, strlen(fpath), fpath+1, 0, NULLP);
(void)free(fpath);
return -(ub == 0);
}
int
util_upload(char *path, FILE *fp)
{
u_long pos;
u_int bytes, first, sent_time;
char *fpath, buf[UBUF_MAXSPACE];
UBUF *ub;
struct timeval starttime;
struct stat wfstat;
uint32_t lastmod;
int blocksize;
if (fstat(fileno(fp), &wfstat) < 0)
{
ffprintf(STDERR,"unexpected error on file descriptor %d: %s\n",
fileno(fp), strerror(errno));
return(-1);
}
(void)gettimeofday(&starttime, (struct timezone *)0);
if (client_trace >= 2)
ffprintf(STDINFO, "uploading `%s' (%d bytes)\n", path, wfstat.st_size);
fpath = util_abs_path(path);
if( packet_limit == 0 && client_buf_len > UBUF_SPACE && !oldserver && version == NULL)
{
/* try to query server for version */
pos=time_out;
time_out=7;
util_get_version();
time_out=pos;
}
/* get correct blocksize */
if (packet_limit > 0)
{
if(client_buf_len >= packet_limit)
blocksize=packet_limit;
else
blocksize=client_buf_len;
}
else
{
if(client_buf_len >= UBUF_SPACE)
blocksize=UBUF_SPACE;
else
blocksize=client_buf_len;
}
for (sent_time = 0, pos = 0, first = 1; ; first = 0)
{
if ((bytes = fread(buf,1,blocksize,fp)) || first)
{
ub = client_interact(CC_UP_LOAD,pos, bytes,buf, 0,NULLP);
if (client_trace == 1 && (udp_sent_time != sent_time))
{
sent_time = udp_sent_time;
if (blocksize >= UBUF_SPACE)
ffprintf(STDINFO,"\r%luk ", 1 + (pos>>10));
else
ffprintf(STDINFO,"\r%lu ", pos);
}
else if (client_trace >= 2)
{
(void)fputc('#', STDINFO);
(void)fflush(STDINFO);
}
}
else
{
/* finish upload */
if (datestamp)
{
lastmod=htonl(wfstat.st_mtime);
ub = client_interact(CC_INSTALL,4,strlen(fpath),fpath+1,4,&lastmod);
}
else
{
ub = client_interact(CC_INSTALL,0,strlen(fpath),fpath+1,0,NULLP);
}
}
if (client_intr_state > 1 || ub == 0)
{
/* remove temporary uploaded file */
ub = client_interact(CC_INSTALL,0,1,"",0,NULLP);
(void)free(fpath);
return 1;
}
if (ub->cmd == CC_ERR)
{
(void)free(fpath);
return(1);
}
if (!bytes && !first)
break;
pos += bytes;
}
if (client_trace)
{
struct timeval endtime;
int millicnt;
(void)gettimeofday(&endtime, (struct timezone *)0);
millicnt = 1000 * (endtime.tv_sec - starttime.tv_sec) +
(endtime.tv_usec - starttime.tv_usec) / 1000;
ffprintf(STDINFO,
"%cdone %lu bytes in %3.01f seconds (%3.02f K/sec)\n",
(client_trace == 1)? '\r': '\n', pos,
(double)millicnt / 1000.0,
millicnt == 0? (double)pos/1024.0
:((double)pos/1024.0) /
((double)millicnt/1000.0));
if (client_trace == 3)
print_comm_stats(STDERR);
(void)fflush(STDERR);
}
(void)free(fpath);
return(0);
}
void
util_get_env(void)
{
char *p, *env_opts;
if ((env_opts = (char *)getenv("FSP_OPTS")))
{
char *colon = (char*)0;
/* ok, ok, so it does more tests than it needs; so what? */
/* at least the indenting is under control... */
/* a good compiler will eliminate all the ones it doesn't need.. :-) */
if (*env_opts)
{
env_host = env_opts;
if((colon = strchr(env_opts, ':')) != (char*)0)
{
*colon = '\0';
env_opts = colon + 1;
}
env_host = strdup(env_host);
}
if (colon && *env_opts)
{
env_port = env_opts;
if((colon = strchr(env_opts, ':')) != (char*)0)
{
*colon = '\0';
env_opts = colon + 1;
}
env_port = strdup(env_port);
}
if (colon && *env_opts)
{
env_myport = env_opts;
if((colon = strchr(env_opts, ':')) != (char*)0)
{
*colon = '\0';
env_opts = colon + 1;
}
env_myport = strdup(env_myport);
}
if (colon && *env_opts)
env_dir = strdup(env_opts);
}
if (!env_host && standalone)
{
if( !(env_host = (char *)getenv("FSP_HOST")) )
{
ffprintf(STDERR,"Env var FSP_HOST not defined\n");
exit(1);
}
else
env_host=strdup(env_host);
}
if (!env_port && standalone)
{
if( !(env_port = (char *)getenv("FSP_PORT")) )
{
ffprintf(STDERR,"Env var FSP_PORT not defined\n");
exit(1);
} else
env_port=strdup(env_port);
}
if (!env_dir && standalone)
{
if(!(env_dir = (char *)getenv("FSP_DIR")))
{
env_dir = strdup("/");
}
else env_dir = strdup(env_dir);
}
if (!env_myport && standalone)
{
if( !(env_myport = (char *)getenv("FSP_LOCALPORT")) )
{
env_myport = strdup("0");
}
else env_myport = strdup(env_myport);
}
if ((char *)getenv("FSP_TRACE"))
client_trace = 1;
if ((p = (char *)getenv("FSP_TIMEOUT")))
time_out = atoi(p);
if ((p = (char *)getenv("FSP_BUF_SIZE")))
{
client_buf_len = atoi(p);
if (client_buf_len > UBUF_MAXSPACE)
client_buf_len = UBUF_MAXSPACE;
else
if (client_buf_len <= 0)
client_buf_len = UBUF_SPACE;
}
client_net_len = htons(client_buf_len);
if ((p = (char *)getenv("FSP_DELAY")))
{
target_delay = atol(p);
if (target_delay < MINDELAY)
target_delay = MINDELAY;
}
if ((p = (char *)getenv("FSP_MAXDELAY")))
{
target_maxdelay = atol(p);
if (target_maxdelay == 0 )
target_maxdelay = DEFAULTMAXDELAY;
if (target_maxdelay < MINDELAY)
target_maxdelay = MINDELAY;
if (target_maxdelay > MAXDELAY)
target_maxdelay = MAXDELAY;
}
}
static RETSIGTYPE
client_intr(int sig)
{
switch(client_intr_state)
{
case 0: exit(2);
case 1: client_intr_state = 2; break;
case 2: exit(3);
}
}
void
env_client(void)
{
util_get_env();
if (init_client(env_host,atoi(env_port),atoi(env_myport)) < 0)
exit(1);
if (isatty(fileno(STDIN)))
STDPROMPT = 0;
(void)signal(SIGINT, client_intr);
}
/*****************************************************************************/
static DDLIST *ddroot = 0;
void
util_flushdir(void)
{
DDLIST *ddp, *ddpnext;
if (ddroot == (DDLIST*)0)
return;
for (ddp = ddroot; ddp; ddp = ddpnext)
{
int i;
ddpnext = ddp->next;
if (ddp->ref_cnt)
{
ffprintf(STDDBG,
"??internal error: flushing directory cache with non-zero referenced directory\n");
ffprintf(STDDBG, "??(dir = %s; ref_cnt = %d)\n",
ddp->path, ddp->ref_cnt);
}
for (i = 0; (ddp->dep_root)[i]; i++)
(void)free((char*)(ddp->dep_root[i]));
(void)free((char*)(ddp->dep_root));
(void)free((char*)(ddp->path));
(void)free((char*)ddp);
}
ddroot = (DDLIST*)0;
}
void
util_dirtydir(char *path)
{
DDLIST *ddp, *ddprev;
int i;
char *fpath;
if (ddroot == (DDLIST*)0)
return;
fpath = util_abs_path(path);
for (ddp = ddroot, ddprev = 0; ddp; ddprev = ddp, ddp = ddp->next)
if (!strcmp(ddp->path,fpath))
break;
(void)free(fpath);
if (ddp == (DDLIST*)0)
return;
if (ddp->ref_cnt)
{
ffprintf(STDDBG,
"??internal error: flushing directory \"%s\" with non-zero reference\n", fpath);
ffprintf(STDDBG, "??(dir = %s; ref_cnt = %d)\n",
ddp->path, ddp->ref_cnt);
}
/* a goodly number of these free's will fail */
for (i = 0; (ddp->dep_root)[i]; i++)
(void)free((char*)(ddp->dep_root[i]));
if (ddprev)
ddprev->next = ddp->next;
else
/* ddp == ddroot */
ddroot = ddp->next;
(void)free((char*)(ddp->dep_root));
(void)free((char*)(ddp->path));
(void)free((char*)ddp);
}
static DDLIST *
util_get_cached_ddlist(char *fpath)
{
DDLIST *ddp;
for (ddp = ddroot; ddp; ddp = ddp->next)
if (!strcmp(ddp->path,fpath))
break;
return ddp;
}
/* it is assumed that util_get_cached_ddlist(fpath) would return (DDLIST*)0 */
static DDLIST *
util_cache_contents(char *fpath)
{
RDIRENT **dep;
DDLIST *ddp;
if (!(dep = get_dir_blk(fpath)))
return (DDLIST*)0;
ddp = (DDLIST*)malloc(sizeof(DDLIST));
ddp->dep_root = dep;
ddp->path = strdup(fpath);
ddp->ref_cnt = 0;
ddp->next = ddroot;
ddroot = ddp;
return ddp;
}
static DDLIST *
util_get_dir(char *fpath)
{
DDLIST *ddp;
ddp = util_get_cached_ddlist(fpath);
if (!ddp)
ddp = util_cache_contents(fpath);
return ddp;
}
RDIR *
util_opendir(char *path)
{
char *fpath;
DDLIST *ddp;
RDIR *rdirp;
fpath = util_abs_path(path);
ddp = util_get_dir(fpath);
(void)free(fpath);
if (!ddp)
return (RDIR*)0;
ddp->ref_cnt++;
rdirp = (RDIR*)malloc(sizeof(RDIR));
rdirp->ddp = ddp;
rdirp->dep = ddp->dep_root;
return rdirp;
}
int
util_closedir(RDIR *rdirp)
{
rdirp->ddp->ref_cnt--;
(void)free(rdirp);
return 0;
}
rdirent *
util_readdir(RDIR *rdirp)
{
static rdirent rde;
RDIRENT **dep;
dep = rdirp->dep;
if (!*dep)
return (rdirent*)0;
rde.rd_fileno = 10;
rde.rd_reclen = 10;
rde.rd_namlen = strlen((*dep)->name);
rde.rd_name = (*dep)->name;
rdirp->dep = dep+1;
return &rde;
}
void
util_split_path(char *path, char **p1, char **p2, char **p3)
{
char *s;
static char junk;
*p1 = "/";
if (*path == '/')
{
*p2 = path;
*p3 = path+1;
}
else
{
*p2 = &junk;
*p3 = path;
}
for (s = *p3; *s; s++)
{
if (*s == '/')
{
*p1 = path;
*p2 = s;
*p3 = s+1;
}
}
if (**p3 == '\0')
*p3 = ".";
}
int
util_stat(const char *path, struct stat *sbuf)
{
RDIR *drp;
RDIRENT **dep;
char *fpath, *ppath, *pfile;
if (!validate_operation(path, UTIL_DIR))
{
errno = ENOENT;
return -1;
}
fpath = util_abs_path(path);
if (!strcmp(fpath,env_dir))
{
ppath = fpath;
pfile = ".";
}
else
{
char *p1;
util_split_path(fpath,&ppath,&p1,&pfile);
*p1 = 0;
}
if ((drp = util_opendir(ppath)))
{
#if 0
/* future expansion stuff... */
char *bmap;
u_int bmap_len;
if (util_pro(ppath, 0, &bmap, &bmaplen, 0, 0) < 0
|| bmap == 0 || bmaplen == 0)
{
/* set defaults */
}
else
{
/* get file information from directory permissions */
if ((bmap[0] & DIR_OWNER))
XXX;
}
#endif
for (dep = drp->dep; *dep; dep++)
{
if (!strcmp((*dep)->name, pfile))
{
if ((*dep)->type & RDTYPE_DIR)
sbuf->st_mode = 0777 | S_IFDIR;
else
sbuf->st_mode = 0666 | S_IFREG;
if ((*dep)->type & RDTYPE_DIR)
sbuf->st_nlink = 2;
else
sbuf->st_nlink = 1;
sbuf->st_uid = 0;
sbuf->st_gid = 0;
sbuf->st_size = (*dep)->size;
sbuf->st_atime = (*dep)->time;
sbuf->st_mtime = (*dep)->time;
sbuf->st_ctime = (*dep)->time;
(void)util_closedir(drp);
(void)free(fpath);
return 0;
}
}
(void)util_closedir(drp);
}
(void)free(fpath);
errno = ENOENT;
return(-1);
}
int
util_cd(const char *p)
{
char *fpath;
DDLIST *ddp;
fpath = util_abs_path(p);
ddp = util_get_dir(fpath);
if (!ddp)
{
(void)free(fpath);
errno = EACCES;
return -1;
}
(void)free(env_dir);
env_dir = fpath;
return 0;
}
/*****************************************************************************/
typedef struct PROLIST
{
struct PROLIST *next;
char *path;
char *text;
char *readme;
u_int readmecnt;
u_int bmaplen;
unsigned char *bmap;
int error;
} PROLIST;
static PROLIST *proroot = 0;
static void
free_prolist(PROLIST *prop)
{
if (prop == 0)
return;
if (prop->path)
(void)free((char *)(prop->path));
if (prop->readme || prop->text)
{
if (prop->readme && prop->readme <= prop->text)
(void)free((char *)(prop->readme - 1));
else
(void)free((char *)(prop->text));
}
if (prop->bmap)
(void)free((char *)(prop->bmap));
(void)free((char *)prop);
}
void
util_flushpro(void)
{
PROLIST *prop, *propnext;
if (proroot == (PROLIST*)0)
return;
for (prop = proroot; prop; prop = propnext)
{
propnext = prop->next;
free_prolist(prop);
}
proroot = (PROLIST*)0;
oldserver = 0;
}
void
util_dirtypro(char *path)
{
PROLIST *prop, *proprev;
char *fpath;
if (proroot == (PROLIST*)0)
return;
fpath = util_abs_path(path);
for (prop = proroot, proprev = 0; prop; proprev = prop, prop = prop->next)
if (!strcmp(prop->path, fpath))
break;
(void)free(fpath);
if (prop == (PROLIST *)0)
return;
if (proprev)
proprev->next = prop->next;
else
proroot = prop->next;
free_prolist(prop);
}
static PROLIST *
util_get_cached_prolist(char *fpath)
{
PROLIST *prop;
for (prop = proroot; prop; prop = prop->next)
if (!strcmp(prop->path, fpath))
break;
return prop;
}
/* it is assumed that util_get_cached_prolist(fpath) would return (PROLIST*)0 */
static PROLIST * util_cache_protection(char *fpath)
{
PROLIST *prop;
UBUF *ub;
ub = client_interact(CC_GET_PRO, 0L, strlen(fpath), fpath + 1, 0, NULLP);
if (client_intr_state > 1)
return 0;
prop = (PROLIST *)malloc(sizeof(PROLIST));
prop->error = (ub->cmd == CC_ERR);
prop->text = strdup(ub->buf);
prop->readme = strchr(prop->text, '\n');
/*
** this may be an old style readme -- if it is, then the first
** character will be '\n'; the free()'ing code will have to clever
** to make sure it free's the least of ->text and ->readme...
*/
if (prop->readme == prop->text)
{
char *eor;
/* old style */
/*
** the first character is '\n', so skip it; (remember this
** when free'ing!). Identify the end of the protection
** readme (the last '\n' I hope), replace it with a '\0',
** and change the ->text to the character after this.
*/
prop->readme++;
eor = strrchr(prop->readme, '\n');
if (eor)
{
*eor = '\0';
prop->text = eor + 1;
}
else
{
/* the protection started with a '\n' but didn't have a README */
prop->text = prop->readme;
}
}
else
{
/* new style, or no readme */
/*
** if there is a readme, change the trailing protection newline
** to a null and skip it and any following '\n's
*/
if (prop->readme)
{
*(prop->readme++) = '\0';
while (*(prop->readme) == '\n')
prop->readme++;
}
}
/* set the count of times that this readme has been read to 0 */
prop->readmecnt = 0;
if (ub->pos == 0)
{
prop->bmap = 0;
prop->bmaplen = 0;
oldserver = 1;
}
else
{
int i;
prop->bmaplen = ub->pos;
if (prop->bmaplen > PRO_BYTES)
prop->bmaplen = PRO_BYTES;
prop->bmap = (char *)malloc(prop->bmaplen);
for (i = 0; i < prop->bmaplen; i++)
prop->bmap[i] = ub->buf[ub->len + i];
/* invert the `PRIV' bit -- it is now the `public' bit */
prop->bmap[0] ^= DIR_PRIV;
oldserver = 0;
}
prop->path = strdup(fpath);
prop->next = proroot;
proroot = prop;
return prop;
}
static int
util_pro(char *path, char **textp,
unsigned char **bmapp, u_int *bmaplenp,
char **readmep, u_int **readmecntpp)
{
PROLIST *prop;
char *fpath;
fpath = util_abs_path(path);
prop = util_get_cached_prolist(fpath);
if (!prop)
prop = util_cache_protection(fpath);
else if (prop->error)
ffprintf(STDERR, "?error: %s\n", prop->text);
(void)free(fpath);
if (textp)
*textp = (prop && !prop->error)? prop->text: 0;
if (bmapp)
*bmapp = (prop && !prop->error)? prop->bmap: 0;
if (bmaplenp)
*bmaplenp = (prop && !prop->error)? prop->bmaplen: 0;
if (readmep)
*readmep = (prop && !prop->error && prop->readme != prop->text)?
prop->readme: 0;
if (readmecntpp)
*readmecntpp = (prop && !prop->error)? &(prop->readmecnt): 0;
return prop? -prop->error: -1;
}
/*****************************************************************************/
/*
** apply a function to a file. apply `process_file' to every file found.
** call `process_dir' to each directory to determine if it is enterable,
** and if it is, enter it, decrementing `recursive', incrementing
** `depth' -- on exit from the directory, call `tidy_dir'. this allows
** the depth of the search to be limited if the value of `recursive' is
** set positive.
*/
int
util_process_file(char *path, int recursive, int depth,
int (*process_file)(char *, struct stat *, int),
int (*process_dir)(char *, int, char **),
void (*tidy_dir)(char *, int, char *))
{
int retval = 0;
struct stat sbuf;
if (util_stat(path, &sbuf) < 0)
{
ffprintf(STDERR, "?remote file `%s': %s\n", path, strerror(errno));
return -1;
}
path = util_abs_path(path);
if (S_ISREG(sbuf.st_mode))
{
if (process_file && sincetime <= sbuf.st_mtime)
retval |= ((*process_file)(path, &sbuf, depth) < 0);
}
else if (S_ISDIR(sbuf.st_mode))
{
if (recursive)
{
char *private_data = 0;
if (process_dir && (*process_dir)(path, depth, &private_data) < 0)
{
retval = 1;
ffprintf(STDERR, "?skipping remote directory `%s'\n", path);
}
else
{
RDIR *rdir;
if ((rdir = util_opendir(path)))
{
struct rdirent *rde;
int pathlen;
pathlen = strlen(path);
while ((rde = util_readdir(rdir)))
{
char *newname;
/* skip over "." and ".." */
if (rde->rd_name[0] == '.'
&& (rde->rd_name[1] == '\0'
|| (rde->rd_name[1] == '.'
&& rde->rd_name[2] == '\0')))
continue;
newname = (char *)malloc(pathlen + rde->rd_namlen + 2);
(void)strcpy(newname, path);
newname[pathlen] = '/';
(void)strcpy(newname + pathlen + 1, rde->rd_name);
retval |= -util_process_file(newname,
recursive - 1, depth + 1,
process_file,
process_dir, tidy_dir);
(void)free(newname);
}
util_closedir(rdir);
}
else
retval = 1;
if (tidy_dir)
(*tidy_dir)(path, depth, private_data);
if (private_data)
(void)free(private_data);
}
}
}
else
{
retval = 1;
ffprintf(STDERR, "?remote file `%s' is not a file or directory!\n",
path);
}
(void)free(path);
return -retval;
}
/*****************************************************************************/
int
util_process_arglist(char **argv, int (*procfn)(char *))
{
int retval = 0;
for ( ; *argv && client_intr_state < 2; argv++)
{
char **globdata, **files, *singlefile[2];
/* this is necessary to allow cd's to directories which can't
be seen -- it would be much nicer if we could change this
to `continue'. */
if (!(globdata = files = glob(*argv)))
{
files = singlefile;
singlefile[0] = *argv;
singlefile[1] = 0;
}
for ( ; *files && client_intr_state < 2; files++)
retval |= ((*procfn)(*files) < 0);
if (globdata)
free_glob(globdata);
}
if (client_intr_state > 1)
retval = 1;
return -retval;
}
/*****************************************************************************/
static void
util_print_readmepro(char *text, u_int *cntp)
{
if (!text || !*text)
ffprintf(STDERR, "?no README\n");
else
{
ffprintf(STDOUT, "-- README:\n%s\n", text);
(*cntp)++;
}
}
void
util_print_readme(void)
{
char *readme;
u_int *readmecntp;
if (util_pro(env_dir, 0, 0, 0, &readme, &readmecntp) < 0)
ffprintf(STDERR, "?cannot access README\n");
else
util_print_readmepro(readme, readmecntp);
}
#define YESNO(v,m) (((v) & (m))? "yes": "no")
static void
util_print_probmap(char *name, char *bmap, int bmaplen)
{
if (!bmap || bmaplen < 1)
return;
ffprintf(STDOUT, "-- directory `%s': ", name);
if(bmap[0] & DIR_OWNER) ffprintf(STDOUT,"You OWN this directory");
ffprintf(STDOUT, "\n");
ffprintf(STDOUT, "\trename: %s", YESNO(bmap[0], DIR_RENAME));
ffprintf(STDOUT, "\t\tdelete: %s", YESNO(bmap[0], DIR_DEL));
ffprintf(STDOUT, "\t\tadd: %s\n", YESNO(bmap[0], DIR_ADD));
ffprintf(STDOUT, "\tmkdir: %s", YESNO(bmap[0], DIR_MKDIR));
ffprintf(STDOUT, "\t\tread: %s", YESNO(bmap[0], DIR_PRIV));
ffprintf(STDOUT, "\t\tlist: %s\n", YESNO(bmap[0], DIR_LIST));
}
int
util_print_protection(char *name)
{
char *text, *bmap, *readme;
u_int bmaplen, *readmecntp;
if (util_pro(name, &text, &bmap, &bmaplen, &readme, &readmecntp) < 0)
{
ffprintf(STDERR, "?cannot get protection of `%s'\n", name);
return -1;
}
if (bmap)
util_print_probmap(name, bmap, bmaplen);
else
ffprintf(STDOUT, "%s: %s\n", name, text);
if (readme && *readme && (readme_max < 0 || *readmecntp < readme_max))
util_print_readmepro(readme, readmecntp);
return 0;
}
/*****************************************************************************/
void
util_dirty_version(void)
{
if (version)
{
(void)free(version);
version = 0;
}
if (verbmap)
{
(void)free(verbmap);
verbmap = 0;
}
verbmaplen = 0;
version_error = 0;
packet_limit = 0;
}
static int
util_get_version(void)
{
UBUF *ub;
ub = client_interact(CC_VERSION, 0L, 0, NULLP, 0, NULLP);
if (client_intr_state > 1 || !ub)
return -1;
util_dirty_version();
version = strdup(ub->buf);
version_error = (ub->cmd == CC_ERR);
if (version_error)
return -1;
if (ub->pos > 0)
{
int i;
verbmaplen = ub->pos;
i = ub->len + 1;
/*
** if we have a thruput bit set, the 4 characters after the first byte
** are the thruput rate. Don't allocate space for them, and skip
** them when copying the bitfield.
*/
if ((ub->buf[ub->len] & VER_THRUPUT))
{
verbmaplen -= 4;
thruput_limit = (u_char)(ub->buf[i++]);
thruput_limit = (thruput_limit << 8) | (u_char)(ub->buf[i++]);
thruput_limit = (thruput_limit << 8) | (u_char)(ub->buf[i++]);
thruput_limit = (thruput_limit << 8) | (u_char)(ub->buf[i++]);
}
/* If we have max packet size appended, parse it and remove it
from the bitmap
*/
if (verbmaplen > VER_BYTES)
{
verbmaplen -= 2;
packet_limit = (u_char)(ub->buf[i++]);
packet_limit = (packet_limit << 8) | (u_char)(ub->buf[i++]);
}
if (verbmaplen > VER_BYTES)
verbmaplen = VER_BYTES;
verbmap = (char *)malloc(verbmaplen);
verbmap[0] = ub->buf[ub->len];
for (i -= ub->len; i < verbmaplen; i++)
verbmap[i] = ub->buf[ub->len + i];
}
return 0;
}
static void
util_print_versionbmap(void)
{
if (!verbmap || verbmaplen < 1)
return;
/* should only print out the first line of the version string */
ffprintf(STDOUT, "%s", version);
ffprintf(STDOUT, "\tlogging: %s",
YESNO(verbmap[0], VER_LOG));
ffprintf(STDOUT, "\t\treadonly: %s\n",
YESNO(verbmap[0], VER_READONLY));
ffprintf(STDOUT, "\treverse naming: %s",
YESNO(verbmap[0], VER_REVNAME));
ffprintf(STDOUT, "\t\tprivate: %s\n",
YESNO(verbmap[0], VER_PRIVMODE));
ffprintf(STDOUT, "\tthruput limited: %s",
YESNO(verbmap[0], VER_THRUPUT));
ffprintf(STDOUT, "\t\textra data: %s\n",
YESNO(verbmap[0], VER_XTRADATA));
if (packet_limit>1024)
{
ffprintf(STDOUT, "\t\tmaximum payload size supported: %d bytes\n",
packet_limit);
}
else if(packet_limit > 0)
{
ffprintf(STDOUT, "\t\tprefered payload size: %d bytes\n",
packet_limit);
}
if ((verbmap[0] & VER_THRUPUT))
ffprintf(STDOUT, "\t\tthruput value: %d KB/sec\n", thruput_limit);
else
ffprintf(STDOUT, "\n");
}
int
util_print_version(void)
{
if (version_error)
{
ffprintf(STDERR, "?error: %s\n", version);
return -1;
}
if (!version && util_get_version() < 0)
return -1;
if (!verbmap)
ffprintf(STDOUT, "%s\n", version);
else
util_print_versionbmap();
return 0;
}
/*****************************************************************************/
#define SERVER_WRITE_OP (DIR_ADD | DIR_MKDIR| DIR_RENAME)
int
validate_operation(const char *name, u_long opmask)
{
char *dirname, *eod;
unsigned char *bmap;
u_int bmaplen;
if (!dbug_flag && (opmask == 0 || standalone || oldserver))
return 1;
if (oldserver && dbug_flag)
ffprintf(STDDBG,
"validate_operation_mask(): old server detected -- debug validate continue\n");
dirname = util_abs_path(name);
if ((opmask & LITERAL_DIR) == 0)
{
eod = strrchr(dirname, '/');
eod[eod == dirname] = '\0';
}
if (dbug_flag > 0)
{
ffprintf(STDDBG,
"validate_operation_mask():\n\tname = `%s';\n\tdirectory = `%s'\n",
name, dirname);
}
if (util_pro(dirname, 0, &bmap, &bmaplen, 0, 0) < 0)
{
if (client_intr_state < 2)
ffprintf(STDWARN, "?cannot get protection of `%s'\n", dirname);
/* it probably means the directory doesn't exist, but... */
/* if we can't get the protection, assume the operation can be done */
(void)free(dirname);
return 0;
}
(void)free(dirname);
/* if this is an old server, then assume the operation is possible */
if (bmap == 0 || bmaplen == 0)
{
if (dbug_flag > 0)
ffprintf(STDDBG, "\t*** OPERATION ALLOWED\n");
return 1;
}
if (dbug_flag > 0)
{
ffprintf(STDDBG,
"\topmask = %08x; bmap[0] = %02x; opmask & bmap[0] = %02x\n",
opmask, bmap[0], opmask & bmap[0]);
}
if ((bmap[0] & DIR_OWNER) || (opmask & bmap[0]) == (opmask & 0xff))
{
int write_needed;
write_needed = (opmask & SERVER_WRITE_OP) != 0;
/*
** if this operation doesn't require a non-readonly server, then
** allow the command;
** or, if this command needs a non-readonly server (i.e., this command
** causes a modification to the remote end) then if we have the
** version string but not a bitmap, allow the command;
** or, if this command needs a non-readonly server, the version string
** is null, and attempting to get the version causes either
** an error or returns no version bitmap, then allow the command.
** otherwise, the command requires a non-readonly server, and we
** have got the version bitmap, so continue.
*/
if (!write_needed
|| (version && !verbmap)
|| (version == 0
&& (util_get_version() < 0 || verbmap == 0)))
{
if (dbug_flag > 0)
ffprintf(STDDBG, "\t*** OPERATION ALLOWED\n");
return 1;
}
/* at this point: write_needed == 1 && verbmap != 0 */
if (dbug_flag > 0)
ffprintf(STDDBG,
"\treadonly = %u; write operation mask = %08x\n",
(verbmap[0] & VER_READONLY) != 0,
opmask & (u_long)SERVER_WRITE_OP);
if ((verbmap[0] & VER_READONLY) == 0)
{
if (dbug_flag > 0)
ffprintf(STDDBG, "\t*** OPERATION ALLOWED\n");
return 1;
}
}
ffprintf(STDERR,
"?operation not allowed on `%s': insufficient permissions\n", name);
return 0;
}
/*****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1