/*********************************************************************\ * 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 #include #include 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; } /*****************************************************************************/